Import 'etherparse' crate

Request Document: go/android-rust-importing-crates
For CL Reviewers: go/android3p#cl-review
For Build Team: go/ab-third-party-imports
Bug: 367704208
Bug: 363022538
Test: m libetherparse

Change-Id: I364c76adf6bf598b1bb70cc6986729e7913424a0
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b354aec
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+Cargo.lock
+target/
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..5b014f2
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,35 @@
+// This file is generated by cargo_embargo.
+// Do not modify this file because the changes will be overridden on upgrade.
+
+package {
+    default_applicable_licenses: ["external_rust_crates_etherparse_license"],
+    default_team: "trendy_team_android_rust",
+}
+
+license {
+    name: "external_rust_crates_etherparse_license",
+    visibility: [":__subpackages__"],
+    license_kinds: ["SPDX-license-identifier-Apache-2.0"],
+    license_text: ["LICENSE"],
+}
+
+rust_library {
+    name: "libetherparse",
+    host_supported: true,
+    crate_name: "etherparse",
+    cargo_env_compat: true,
+    cargo_pkg_version: "0.16.0",
+    crate_root: "src/lib.rs",
+    edition: "2021",
+    features: [
+        "default",
+        "std",
+    ],
+    rustlibs: ["libarrayvec"],
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    product_available: true,
+    vendor_available: true,
+}
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..97719a6
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,92 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2021"
+name = "etherparse"
+version = "0.16.0"
+authors = ["Julian Schmid <[email protected]>"]
+build = false
+exclude = [
+    ".gitignore",
+    ".travis.yml",
+    ".github/*",
+    ".gitlab-ci.yml",
+    ".travis/*",
+    "appveyor.yml",
+]
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
+description = "A library for parsing & writing a bunch of packet based protocols (EthernetII, IPv4, IPv6, UDP, TCP ...)."
+readme = "README.md"
+keywords = [
+    "ipv4",
+    "ipv6",
+    "vlan",
+    "udp",
+    "tcp",
+]
+categories = [
+    "network-programming",
+    "parser-implementations",
+    "no-std",
+    "no-std::no-alloc",
+]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/JulianSchmid/etherparse"
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = [
+    "--cfg",
+    "docsrs",
+]
+
+[lib]
+name = "etherparse"
+path = "src/lib.rs"
+
+[[example]]
+name = "ip_defrag"
+path = "examples/ip_defrag.rs"
+
+[[example]]
+name = "read_by_slicing"
+path = "examples/read_by_slicing.rs"
+
+[[example]]
+name = "write_ipv4_udp"
+path = "examples/write_ipv4_udp.rs"
+
+[[example]]
+name = "write_tcp"
+path = "examples/write_tcp.rs"
+
+[[example]]
+name = "write_udp"
+path = "examples/write_udp.rs"
+
+[[test]]
+name = "unit-tests"
+path = "tests/unit-tests.rs"
+
+[dependencies.arrayvec]
+version = "0.7.2"
+default-features = false
+
+[dev-dependencies.proptest]
+version = "1.4.0"
+
+[features]
+default = ["std"]
+std = ["arrayvec/std"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..64a338d
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,33 @@
+[package]
+name = "etherparse"
+version = "0.16.0"
+authors = ["Julian Schmid <[email protected]>"]
+edition = "2021"
+repository = "https://github.com/JulianSchmid/etherparse"
+description = "A library for parsing & writing a bunch of packet based protocols (EthernetII, IPv4, IPv6, UDP, TCP ...)."
+categories = ["network-programming", "parser-implementations", "no-std", "no-std::no-alloc"]
+keywords = ["ipv4", "ipv6", "vlan", "udp", "tcp"]
+license = "MIT OR Apache-2.0"
+readme = "../README.md"
+exclude = [
+    ".gitignore",
+    ".travis.yml",
+    ".github/*",
+    ".gitlab-ci.yml",
+    ".travis/*",
+    "appveyor.yml"
+]
+
+[features]
+default = ["std"]
+std = ["arrayvec/std"]
+
+[dependencies]
+arrayvec = { version = "0.7.2", default-features = false }
+
+[dev-dependencies]
+proptest = "1.4.0"
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..4c889c7
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2024 Julian Schmid
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
\ No newline at end of file
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..21d9f42
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,17 @@
+name: "etherparse"
+description: "A library for parsing & writing a bunch of packet based protocols (EthernetII, IPv4, IPv6, UDP, TCP ...)."
+third_party {
+  version: "0.16.0"
+  license_type: NOTICE
+  last_upgrade_date {
+    year: 2024
+    month: 9
+    day: 30
+  }
+  homepage: "https://crates.io/crates/etherparse"
+  identifier {
+    type: "Archive"
+    value: "https://static.crates.io/crates/etherparse/etherparse-0.16.0.crate"
+    version: "0.16.0"
+  }
+}
\ No newline at end of file
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
new file mode 120000
index 0000000..7a694c9
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1 @@
+LICENSE
\ No newline at end of file
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..6183b85
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1 @@
+include platform/prebuilts/rust:main:/OWNERS
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..19a2505
--- /dev/null
+++ b/README.md
@@ -0,0 +1,248 @@
+[![Crates.io](https://img.shields.io/crates/v/etherparse.svg)](https://crates.io/crates/etherparse)
+[![docs.rs](https://docs.rs/etherparse/badge.svg)](https://docs.rs/etherparse)
+[![Build Status Github](https://github.com/JulianSchmid/etherparse/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/JulianSchmid/etherparse/actions/workflows/main.yml)
+[![Build Status Gitlab](https://gitlab.com/julian.schmid/etherparse/badges/master/pipeline.svg)](https://gitlab.com/julian.schmid/etherparse/-/commits/master)
+[![Codecov](https://codecov.io/gh/JulianSchmid/etherparse/branch/master/graph/badge.svg?token=yjfRLgScR6)](https://codecov.io/gh/JulianSchmid/etherparse)
+
+# etherparse
+
+A zero allocation supporting library for parsing & writing a bunch of packet based protocols (EthernetII, IPv4, IPv6, UDP, TCP ...).
+
+Currently supported are:
+* Ethernet II
+* IEEE 802.1Q VLAN Tagging Header
+* IPv4
+* IPv6 (supporting the most common extension headers, but not all)
+* UDP
+* TCP
+* ICMP & ICMPv6 (not all message types are supported)
+
+Reconstruction of fragmented IP packets is also supported, but requires allocations.
+
+## Usage
+
+Add the following to your `Cargo.toml`:
+
+```toml
+[dependencies]
+etherparse = "0.16"
+```
+
+## What is etherparse?
+Etherparse is intended to provide the basic network parsing functions that allow for easy analysis, transformation or generation of recorded network data.
+
+Some key points are:
+
+* It is completely written in Rust and thoroughly tested.
+* Special attention has been paid to not use allocations or syscalls except in the "defragmentation" code.
+* The package is still in development and can & will still change.
+* The current focus of development is on the most popular protocols in the internet & transport layer.
+
+## How to parse network packages?
+Etherparse gives you two options for parsing network packages automatically:
+
+### Slicing the packet
+Here the different components in a packet are separated without parsing all their fields. For each header a slice is generated that allows access to the fields of a header.
+```rust
+match SlicedPacket::from_ethernet(&packet) {
+    Err(value) => println!("Err {:?}", value),
+    Ok(value) => {
+        println!("link: {:?}", value.link);
+        println!("vlan: {:?}", value.vlan);
+        println!("net: {:?}", value.net); // contains ip
+        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.
+
+Depending from which point downward you want to slice a package check out the functions:
+
+* [`SlicedPacket::from_ethernet`](https://docs.rs/etherparse/~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/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/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/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/etherparse/struct.LaxSlicedPacket.html#method.from_ethernet) for parsing from an Ethernet II header downwards
+* [`LaxSlicedPacket::from_ether_type`](https://docs.rs/etherparse/~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/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.
+```rust
+match PacketHeaders::from_ethernet_slice(&packet) {
+    Err(value) => println!("Err {:?}", value),
+    Ok(value) => {
+        println!("link: {:?}", value.link);
+        println!("vlan: {:?}", value.vlan);
+        println!("net: {:?}", value.net); // contains ip
+        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.
+
+Depending from which point downward you want to unpack a package check out the functions
+
+* [`PacketHeaders::from_ethernet_slice`](https://docs.rs/etherparse/~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/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/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/etherparse/struct.LaxPacketHeaders.html#method.from_ethernet) for parsing from an Ethernet II header downwards
+* [`LaxPacketHeaders::from_ether_type`](https://docs.rs/etherparse/~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/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/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)
+* [`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)
+* [`Ipv6Slice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6Slice.html#method.from_slice) & [`LaxIpv6Slice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.LaxIpv6Slice.html#method.from_slice)
+* [`UdpSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.UdpSlice.html#method.from_slice) & [`UdpSlice::from_slice_lax`](https://docs.rs/etherparse/~0/etherparse/struct.UdpSlice.html#method.from_slice_lax)
+* [`TcpSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.TcpSlice.html#method.from_slice)
+* [`Icmpv4Slice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv4Slice.html#method.from_slice)
+* [`Icmpv6Slice::from_slice`](https://docs.rs/etherparse/~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
+payload (e.g. the payload of IPv6 packets will be limited by the "payload length" field in
+an IPv6 header).
+
+### Manually slicing & parsing only headers
+
+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/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)
+* [`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)
+* [`Ipv6ExtensionsSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6ExtensionsSlice.html#method.from_slice)
+* [`Ipv6RawExtHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6RawExtHeaderSlice.html#method.from_slice)
+* [`IpAuthHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.IpAuthHeaderSlice.html#method.from_slice)
+* [`Ipv6FragmentHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6FragmentHeaderSlice.html#method.from_slice)
+* [`UdpHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.UdpHeaderSlice.html#method.from_slice)
+* [`TcpHeaderSlice::from_slice`](https://docs.rs/etherparse/~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/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)
+* [`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)
+* [`Ipv4Extensions::read`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Extensions.html#method.read) & [`Ipv4Extensions::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Extensions.html#method.from_slice)
+* [`Ipv6Header::read`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6Header.html#method.read) & [`Ipv6Header::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6Header.html#method.from_slice)
+* [`Ipv6Extensions::read`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6Extensions.html#method.read) & [`Ipv6Extensions::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6Extensions.html#method.from_slice)
+* [`Ipv6RawExtHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6RawExtHeader.html#method.read) & [`Ipv6RawExtHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6RawExtHeader.html#method.from_slice)
+* [`IpAuthHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.IpAuthHeader.html#method.read) & [`IpAuthHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.IpAuthHeader.html#method.from_slice)
+* [`Ipv6FragmentHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6FragmentHeader.html#method.read) & [`Ipv6FragmentHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6FragmentHeader.html#method.from_slice)
+* [`UdpHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.UdpHeader.html#method.read) & [`UdpHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.UdpHeader.html#method.from_slice)
+* [`TcpHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.TcpHeader.html#method.read) & [`TcpHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.TcpHeader.html#method.from_slice)
+* [`Icmpv4Header::read`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv4Header.html#method.read) & [`Icmpv4Header::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv4Header.html#method.from_slice)
+* [`Icmpv6Header::read`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv6Header.html#method.read) & [`Icmpv6Header::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv6Header.html#method.from_slice)
+
+## How to generate fake packet data?
+
+### Packet Builder
+
+The PacketBuilder struct provides a high level interface for quickly creating network packets. The PacketBuilder will automatically set fields which can be deduced from the content and compositions of the packet itself (e.g. checksums, lengths, ethertype, ip protocol number).
+
+[Example:](etherparse/examples/write_udp.rs)
+```rust
+use etherparse::PacketBuilder;
+
+let builder = PacketBuilder::
+    ethernet2([1,2,3,4,5,6],     //source mac
+               [7,8,9,10,11,12]) //destination mac
+    .ipv4([192,168,1,1], //source ip
+          [192,168,1,2], //destination ip
+          20)            //time to life
+    .udp(21,    //source port
+         1234); //destination port
+
+//payload of the udp packet
+let payload = [1,2,3,4,5,6,7,8];
+
+//get some memory to store the result
+let mut result = Vec::<u8>::with_capacity(builder.size(payload.len()));
+
+//serialize
+//this will automatically set all length fields, checksums and identifiers (ethertype & protocol)
+//before writing the packet out to "result"
+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/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/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)
+* [`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)
+* [`Ipv4Extensions::write`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Extensions.html#method.write)
+* [`Ipv6Header::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6Header.html#method.to_bytes) & [`Ipv6Header::write`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6Header.html#method.write)
+* [`Ipv6Extensions::write`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6Extensions.html#method.write)
+* [`Ipv6RawExtHeader::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6RawExtHeader.html#method.to_bytes) & [`Ipv6RawExtHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6RawExtHeader.html#method.write)
+* [`IpAuthHeader::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.IpAuthHeader.html#method.to_bytes) & [`IpAuthHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.IpAuthHeader.html#method.write)
+* [`Ipv6FragmentHeader::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6FragmentHeader.html#method.to_bytes) & [`Ipv6FragmentHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6FragmentHeader.html#method.write)
+* [`UdpHeader::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.UdpHeader.html#method.to_bytes) & [`UdpHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.UdpHeader.html#method.write)
+* [`TcpHeader::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.TcpHeader.html#method.to_bytes) & [`TcpHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.TcpHeader.html#method.write)
+* [`Icmpv4Header::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv4Header.html#method.to_bytes) & [`Icmpv4Header::write`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv4Header.html#method.write)
+* [`Icmpv6Header::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv6Header.html#method.to_bytes) & [`Icmpv6Header::write`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv6Header.html#method.write)
+
+## References
+* Darpa Internet Program Protocol Specification [RFC 791](https://tools.ietf.org/html/rfc791)
+* Internet Protocol, Version 6 (IPv6) Specification [RFC 8200](https://tools.ietf.org/html/rfc8200)
+* [IANA Protocol Numbers](https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml)
+* [Internet Protocol Version 6 (IPv6) Parameters](https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml)
+* [Wikipedia IEEE_802.1Q](https://en.wikipedia.org/w/index.php?title=IEEE_802.1Q&oldid=820983900)
+* User Datagram Protocol (UDP) [RFC 768](https://tools.ietf.org/html/rfc768)
+* Transmission Control Protocol [RFC 793](https://tools.ietf.org/html/rfc793)
+* TCP Extensions for High Performance [RFC 7323](https://tools.ietf.org/html/rfc7323)
+* The Addition of Explicit Congestion Notification (ECN) to IP [RFC 3168](https://tools.ietf.org/html/rfc3168)
+* Robust Explicit Congestion Notification (ECN) Signaling with Nonces [RFC 3540](https://tools.ietf.org/html/rfc3540)
+* IP Authentication Header [RFC 4302](https://tools.ietf.org/html/rfc4302)
+* Mobility Support in IPv6 [RFC 6275](https://tools.ietf.org/html/rfc6275)
+* Host Identity Protocol Version 2 (HIPv2) [RFC 7401](https://tools.ietf.org/html/rfc7401)
+* Shim6: Level 3 Multihoming Shim Protocol for IPv6 [RFC 5533](https://tools.ietf.org/html/rfc5533)
+* Computing the Internet Checksum [RFC 1071](https://datatracker.ietf.org/doc/html/rfc1071)
+* Internet Control Message Protocol [RFC 792](https://datatracker.ietf.org/doc/html/rfc792)
+* [IANA Internet Control Message Protocol (ICMP) Parameters](https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml)
+* Requirements for Internet Hosts -- Communication Layers [RFC 1122](https://datatracker.ietf.org/doc/html/rfc1122)
+* Requirements for IP Version 4 Routers [RFC 1812](https://datatracker.ietf.org/doc/html/rfc1812)
+* Internet Control Message Protocol (ICMPv6) for the Internet Protocol Version 6 (IPv6) Specification [RFC 4443](https://datatracker.ietf.org/doc/html/rfc4443)
+* ICMP Router Discovery Messages [RFC 1256](https://datatracker.ietf.org/doc/html/rfc1256)
+* [Internet Control Message Protocol version 6 (ICMPv6) Parameters](https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml)
+* Multicast Listener Discovery (MLD) for IPv6 [RFC 2710](https://datatracker.ietf.org/doc/html/rfc2710)
+* Neighbor Discovery for IP version 6 (IPv6) [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)
+* [LINKTYPE_LINUX_SLL](https://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html) on tcpdump
+* LINUX_SLL [header definition](https://github.com/the-tcpdump-group/libpcap/blob/a932566fa1f6df16176ac702b1762ea1cd9ed9a3/pcap/sll.h) on libpcap
+* [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 
+
+## 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.
+
+### Contribution
+Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be licensed as above, without any additional terms or conditions.
diff --git a/cargo_embargo.json b/cargo_embargo.json
new file mode 100644
index 0000000..16ab9c3
--- /dev/null
+++ b/cargo_embargo.json
@@ -0,0 +1,3 @@
+{
+  "tests": false
+}
diff --git a/examples/ip_defrag.rs b/examples/ip_defrag.rs
new file mode 100644
index 0000000..111e2b6
--- /dev/null
+++ b/examples/ip_defrag.rs
@@ -0,0 +1,78 @@
+use etherparse::*;
+
+fn main() {
+    // setup some network data to parse
+    let builder = PacketBuilder::ethernet2(
+        // source mac
+        [1, 2, 3, 4, 5, 6],
+        // destination mac
+        [7, 8, 9, 10, 11, 12],
+    )
+    .ip(IpHeaders::Ipv4(
+        Ipv4Header {
+            total_len: 0, // will be overwritten by builder
+            identification: 1234,
+            dont_fragment: false,
+            more_fragments: true,
+            fragment_offset: IpFragOffset::try_new(1024 / 8).unwrap(),
+            time_to_live: 20,
+            protocol: IpNumber::UDP,
+            header_checksum: 0, // will be overwritten by builder
+            source: [1, 2, 3, 4],
+            destination: [2, 3, 4, 5],
+            ..Default::default()
+        },
+        Default::default(),
+    ))
+    .udp(
+        21,   // source port
+        1234, // desitnation port
+    );
+
+    // payload of the udp packet
+    let payload = [1, 2, 3, 4, 5, 6, 7, 8];
+
+    // get some memory to store the serialized data
+    let mut serialized = Vec::<u8>::with_capacity(builder.size(payload.len()));
+    builder.write(&mut serialized, &payload).unwrap();
+
+    // pool that manages the different fragmented packets & the different memory buffers for re-assembly
+    let mut ip_defrag_pool = defrag::IpDefragPool::<(), ()>::new();
+
+    // slice the packet into the different header components
+    let sliced_packet = match SlicedPacket::from_ethernet(&serialized) {
+        Err(err) => {
+            println!("Err {:?}", err);
+            return;
+        }
+        Ok(v) => v,
+    };
+
+    // constructed
+    if sliced_packet.is_ip_payload_fragmented() {
+        let defrag_result = ip_defrag_pool.process_sliced_packet(&sliced_packet, (), ());
+        match defrag_result {
+            Ok(Some(finished)) => {
+                println!(
+                    "Successfully reconstructed fragmented IP packet ({} bytes, protocol {:?})",
+                    finished.payload.len(),
+                    finished.ip_number,
+                );
+
+                // continue parsing the payload
+                // ... fill in your code here
+
+                // IMPORTANT: After done return the finished packet buffer to avoid unneeded allocations
+                ip_defrag_pool.return_buf(finished);
+            }
+            Ok(None) => {
+                println!(
+                    "Received a fragmented packet, but the reconstruction was not yet finished"
+                );
+            }
+            Err(err) => {
+                println!("Error reconstructing fragmented IPv4 packet: {err}");
+            }
+        }
+    }
+}
diff --git a/examples/read_by_slicing.rs b/examples/read_by_slicing.rs
new file mode 100644
index 0000000..f9299da
--- /dev/null
+++ b/examples/read_by_slicing.rs
@@ -0,0 +1,120 @@
+use etherparse::*;
+
+fn main() {
+    //setup some network data to parse
+    let builder = PacketBuilder::ethernet2(
+        //source mac
+        [1, 2, 3, 4, 5, 6],
+        //destination mac
+        [7, 8, 9, 10, 11, 12],
+    )
+    .ipv4(
+        //source ip
+        [192, 168, 1, 1],
+        //destination ip
+        [192, 168, 1, 2],
+        //time to life
+        20,
+    )
+    .udp(
+        21,   //source port
+        1234, //desitnation port
+    );
+
+    //payload of the udp packet
+    let payload = [1, 2, 3, 4, 5, 6, 7, 8];
+
+    //get some memory to store the serialized data
+    let mut serialized = Vec::<u8>::with_capacity(builder.size(payload.len()));
+    builder.write(&mut serialized, &payload).unwrap();
+
+    //slice the packet into the different header components
+    let sliced_packet = SlicedPacket::from_ethernet(&serialized);
+
+    //print some informations about the sliced packet
+    match sliced_packet {
+        Err(value) => println!("Err {:?}", value),
+        Ok(value) => {
+            println!("Ok");
+            use etherparse::{LinkSlice::*, NetSlice::*, TransportSlice::*, VlanSlice::*};
+
+            match value.link {
+                Some(Ethernet2(value)) => println!(
+                    "  Ethernet2 {:?} => {:?}",
+                    value.source(),
+                    value.destination()
+                ),
+                Some(LinuxSll(value)) => println!(
+                    "  LinuxSll (packet type: {:?}, source address: {:?})",
+                    value.packet_type(),
+                    value.sender_address(),
+                ),
+                Some(EtherPayload(payload)) => {
+                    println!("  EtherPayload (ether type {:?})", payload.ether_type)
+                }
+                Some(LinuxSllPayload(payload)) => {
+                    println!(
+                        "  LinuxSllPayload (protocol type {:?})",
+                        payload.protocol_type
+                    )
+                }
+                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 => {}
+            }
+
+            match value.net {
+                Some(Ipv4(ipv4)) => {
+                    println!(
+                        "  Ipv4 {:?} => {:?}",
+                        ipv4.header().source_addr(),
+                        ipv4.header().destination_addr()
+                    );
+                    if false == ipv4.extensions().is_empty() {
+                        println!("    {:?}", ipv4.extensions());
+                    }
+                }
+                Some(Ipv6(ipv6)) => {
+                    println!(
+                        "  Ipv6 {:?} => {:?}",
+                        ipv6.header().source_addr(),
+                        ipv6.header().destination_addr()
+                    );
+                    if false == ipv6.extensions().is_empty() {
+                        println!("    {:?}", ipv6.extensions());
+                    }
+                }
+                None => {}
+            }
+
+            match value.transport {
+                Some(Icmpv4(value)) => println!(" Icmpv4 {:?}", value),
+                Some(Icmpv6(value)) => println!(" Icmpv6 {:?}", value),
+                Some(Udp(value)) => println!(
+                    "  UDP {:?} -> {:?}",
+                    value.source_port(),
+                    value.destination_port()
+                ),
+                Some(Tcp(value)) => {
+                    println!(
+                        "  TCP {:?} -> {:?}",
+                        value.source_port(),
+                        value.destination_port()
+                    );
+                    let options: Vec<Result<TcpOptionElement, TcpOptionReadError>> =
+                        value.options_iterator().collect();
+                    println!("    {:?}", options);
+                }
+                None => {}
+            }
+        }
+    }
+}
diff --git a/examples/write_ipv4_udp.rs b/examples/write_ipv4_udp.rs
new file mode 100644
index 0000000..09dbb99
--- /dev/null
+++ b/examples/write_ipv4_udp.rs
@@ -0,0 +1,74 @@
+use etherparse::*;
+use std::io::Write;
+
+fn main() {
+    let with_udp_checksum = true;
+
+    //Any struct implementing the "Write" trait can be used to write to (e.g. File).
+    //For this example lets use a simple Vec as it implements the write trait.
+    let mut out = Vec::<u8>::with_capacity(
+        //lets reserve enough memory to avoid unnecessary allocations
+        Ethernet2Header::LEN + Ipv4Header::MAX_LEN + UdpHeader::LEN + 8, //payload
+    );
+
+    //setup the actual payload of the udp packet
+    let udp_payload = [1, 2, 3, 4, 5, 6, 7, 8];
+
+    //Lets start out with an ethernet II header containing the mac addresses
+    Ethernet2Header {
+        destination: [1, 2, 3, 4, 5, 6],
+        source: [11, 12, 13, 14, 15, 16],
+        ether_type: ether_type::IPV4,
+    }
+    .write(&mut out)
+    .unwrap();
+
+    //create the ipv4 header with the helper function
+    //Note: It is also possible to define the rest of the header values via Ipv4Header {...}
+    let ip_header = Ipv4Header::new(
+        //payload length
+        (UdpHeader::LEN + udp_payload.len()) as u16,
+        20,                //time to live
+        ip_number::UDP,    //contained protocol is udp
+        [192, 168, 1, 42], //source ip address
+        [192, 168, 1, 1],  //destination ip address
+    )
+    .unwrap();
+
+    //write the ipv4 header
+    //
+    //The "write" call automatically calculates the ipv4 checksum.
+    //Alternatively "write_raw" can be used to skip the checksum
+    //calculation and just write out the checksum set in the header.
+    ip_header.write(&mut out).unwrap();
+
+    //write the udp header
+    //
+    //There is the option to write it with a checksum or without.
+    //If yes, the ipv4 header & payload are needed to calculate the header
+    if with_udp_checksum {
+        UdpHeader::with_ipv4_checksum(
+            0,            //source port
+            42,           //destination port
+            &ip_header,   //ip header
+            &udp_payload, //udp payload
+        )
+        .unwrap()
+        .write(&mut out)
+        .unwrap();
+    } else {
+        //write the header with the checksum disabled
+        UdpHeader::without_ipv4_checksum(
+            0,                 //source port
+            42,                //destination port
+            udp_payload.len(), //payload length
+        )
+        .unwrap()
+        .write(&mut out)
+        .unwrap();
+    }
+
+    out.write_all(&udp_payload).unwrap();
+
+    println!("{:?}", &out);
+}
diff --git a/examples/write_tcp.rs b/examples/write_tcp.rs
new file mode 100644
index 0000000..3c270a6
--- /dev/null
+++ b/examples/write_tcp.rs
@@ -0,0 +1,42 @@
+use etherparse::*;
+
+fn main() {
+    //setup the packet headers
+    let builder = PacketBuilder::ethernet2(
+        [1, 2, 3, 4, 5, 6],    //source mac
+        [7, 8, 9, 10, 11, 12], //destination mac
+    )
+    .ipv4(
+        [192, 168, 1, 1], //source ip
+        [192, 168, 1, 2], //destination ip
+        20,               //time to life
+    )
+    .tcp(
+        21,    //source port
+        1234,  //desitnation port
+        1,     //sequence number
+        26180, //window size
+    )
+    //set additional tcp header fields
+    //supported flags: ns(), fin(), syn(), rst(), psh(), ece(), cwr()
+    .ns() //set the ns flag
+    .ack(123) //ack flag + the ack number
+    .urg(23) //urg flag + urgent pointer
+    //tcp header options
+    .options(&[
+        TcpOptionElement::Noop,
+        TcpOptionElement::MaximumSegmentSize(1234),
+    ])
+    .unwrap();
+
+    //payload of the tcp packet
+    let payload = [1, 2, 3, 4, 5, 6, 7, 8];
+
+    //get some memory to store the result
+    let mut result = Vec::<u8>::with_capacity(builder.size(payload.len()));
+
+    //serialize
+    //this will automatically set all length fields, checksums and identifiers (ethertype & protocol)
+    builder.write(&mut result, &payload).unwrap();
+    println!("{:?}", result);
+}
diff --git a/examples/write_udp.rs b/examples/write_udp.rs
new file mode 100644
index 0000000..b3a2ea7
--- /dev/null
+++ b/examples/write_udp.rs
@@ -0,0 +1,29 @@
+use etherparse::*;
+
+fn main() {
+    //setup the packet headers
+    let builder = PacketBuilder::ethernet2(
+        [1, 2, 3, 4, 5, 6],    //source mac
+        [7, 8, 9, 10, 11, 12], //destination mac
+    )
+    .ipv4(
+        [192, 168, 1, 1], //source ip
+        [192, 168, 1, 2], //destination ip
+        20,               //time to life
+    )
+    .udp(
+        21,   //source port
+        1234, //desitnation port
+    );
+
+    //payload of the udp packet
+    let payload = [1, 2, 3, 4, 5, 6, 7, 8];
+
+    //get some memory to store the result
+    let mut result = Vec::<u8>::with_capacity(builder.size(payload.len()));
+
+    //serialize
+    //this will automatically set all length fields, checksums and identifiers (ethertype & protocol)
+    builder.write(&mut result, &payload).unwrap();
+    println!("{:?}", result);
+}
diff --git a/proptest-regressions/compositions_tests.txt b/proptest-regressions/compositions_tests.txt
new file mode 100644
index 0000000..6bd98db
--- /dev/null
+++ b/proptest-regressions/compositions_tests.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 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]
diff --git a/proptest-regressions/internet/internet_slice.txt b/proptest-regressions/internet/internet_slice.txt
new file mode 100644
index 0000000..cd45a89
--- /dev/null
+++ b/proptest-regressions/internet/internet_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 97770562c27d77b6712b809b566ffe400df2b2ca3046e643e5f2166090f4a327 # shrinks to ipv4_header = 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: IpFragOffset(0), time_to_live: 0, protocol: 68 (any distributed file system), header_checksum: 0, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [0, 0, 0, 0, 0, 0, 0, 0] }, ipv4_exts = Ipv4Extensions { auth: None }, ipv6_header = Ipv6Header { traffic_class: 22, flow_label: 395206, payload_length: 37958, next_header: 172, hop_limit: 137, source: [99, 186, 12, 226, 117, 70, 74, 128, 47, 61, 103, 116, 125, 57, 68, 219], destination: [98, 36, 139, 184, 238, 110, 147, 161, 222, 44, 184, 124, 31, 106, 87, 248] }, mut ipv6_exts = Ipv6Extensions { hop_by_hop_options: None, destination_options: None, routing: None, fragment: Some(Ipv6FragmentHeader { next_header: 51 (AH - Authentication Header), fragment_offset: IpFragOffset(3576), more_fragments: false, identification: 1363142016 }), auth: Some(IpAuthHeader { next_header: 17 (UDP - User Datagram), spi: 1791610029, sequence_number: 2584472886, raw_icv: [7, 228, 240, 72, 15, 159, 71, 243, 221, 88, 254, 85, 113, 158, 208, 119, 138, 203, 182, 77, 92, 141, 251, 112, 9, 236, 219, 62, 192, 216, 161, 123, 217, 224, 41, 71, 237, 94, 1, 168, 60, 169, 86, 194, 107, 100, 14, 14, 216, 69, 195, 15, 26, 202, 77, 165, 218, 96, 70, 196, 205, 188, 187, 22, 118, 245, 83, 55, 19, 84, 36, 109, 57, 159, 76, 130, 254, 218, 187, 15, 47, 25, 146, 206, 196, 30, 109, 191, 120, 59, 67, 90, 203, 164, 2, 89, 23, 70, 31, 117, 83, 183, 66, 100, 71, 159, 205, 94, 88, 228, 189, 28, 71, 187, 44, 122, 166, 87, 26, 203, 191, 13, 205, 81, 3, 195, 92, 205, 71, 152, 182, 219, 223, 44, 135, 213, 153, 71, 3, 156, 71, 101, 176, 142, 8, 51, 101, 88, 147, 0, 198, 251, 50, 170, 117, 197, 39, 183, 212, 96, 213, 114, 128, 23, 99, 182, 244, 184, 141, 178, 127, 231, 219, 255, 135, 224, 53, 223, 160, 249, 204, 18, 49, 17, 167, 182, 34, 238, 124, 231, 68, 235, 124, 216, 57, 194, 68, 93, 109, 7, 166, 144, 41, 33, 118, 246, 195, 215, 237, 108, 241, 45, 37, 45, 46, 245, 234, 14, 153, 180, 4, 67, 225, 223, 19, 111, 164, 165, 86, 221, 202, 231, 73, 24, 34, 227, 36, 57, 164, 167, 169, 39, 171, 13, 76, 109, 191, 249, 199, 3, 239, 109, 230, 59, 142, 234, 28, 2, 145, 43, 57, 212, 46, 71, 167, 171, 195, 237, 52, 106, 173, 173, 128, 143, 207, 75, 171, 183, 130, 79, 107, 218, 3, 174, 90, 173, 137, 182, 64, 85, 227, 103, 149, 209, 113, 20, 191, 39, 124, 223, 204, 112, 204, 242, 239, 100, 156, 214, 5, 164, 125, 77, 196, 79, 209, 199, 11, 20, 185, 242, 42, 203, 168, 236, 99, 79, 112, 107, 2, 166, 74, 213, 92, 175, 209, 72, 117, 85, 1, 155, 116, 53, 191, 107] }) }
diff --git a/proptest-regressions/internet/ip_auth_header.txt b/proptest-regressions/internet/ip_auth_header.txt
new file mode 100644
index 0000000..8bdc4d4
--- /dev/null
+++ b/proptest-regressions/internet/ip_auth_header.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 159267836a648eca852fb99cd2e2035279bb389560c8c24dd3ab89d94137459f # shrinks to header = IpAuthHeader { next_header: 207, spi: 0, sequence_number: 0, raw_icv: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 128, 239, 235, 139, 103, 67, 20, 232, 165, 130, 99, 126, 233, 46, 188, 157, 107, 12, 135, 213, 227, 67, 185, 217, 225, 211, 10, 234, 108, 88, 143, 27, 101, 85, 25, 92, 97, 90, 125, 18, 226, 33, 191, 38, 57, 63, 224, 218, 67, 173, 204, 203, 213, 167, 137, 211, 87, 3, 149, 118, 79, 53, 73, 34, 170, 244, 128, 65, 21, 49, 16, 91, 187, 241, 224, 110, 29, 151, 0, 174, 142, 125, 43, 53, 95, 30, 148, 62, 172, 243, 223, 244, 83, 131, 178, 10, 125, 228, 74, 122, 193, 13, 222, 196, 119, 215, 151, 164, 14, 149, 253, 206, 249, 144, 225, 65, 169, 119, 176, 199, 35, 211, 24, 157, 187, 131, 241, 184, 158, 0, 89, 56, 207, 171, 12, 233, 155, 247, 101, 15, 81, 134, 249, 223, 162, 64, 15, 78, 222, 241, 92, 92, 231, 154, 51, 90, 82, 139, 19, 54, 72, 54, 204, 37, 27, 51, 125, 119, 254, 131, 186, 145, 217, 206, 225, 227, 205, 224, 192, 167, 205, 210, 214, 45, 165, 151, 225, 171, 198, 21, 83, 32, 226, 220, 123, 56, 138, 64, 240, 51, 174, 209, 59, 185, 3, 66, 159, 218, 131, 10, 110, 35, 122, 187, 187, 62, 231, 57, 54, 38, 81, 126, 164, 232, 5, 190, 95, 80, 68, 215, 213, 187, 159, 124, 227, 13, 147, 192, 41, 97, 30, 48, 130, 9, 215, 89, 146, 233, 40, 234, 76, 182, 6, 70, 54, 83, 102, 242, 111, 10, 5, 7, 54, 218, 29, 204, 1, 54, 68, 65, 231, 59, 235, 64, 209, 185, 14, 201, 254, 125, 89, 29, 245, 116, 77, 3, 18, 241, 167, 140, 96, 254, 227, 67, 141, 225, 244, 22, 78, 116, 20, 245, 244, 30, 47, 5, 172, 214, 93, 191, 222, 96, 209, 179, 174, 184, 179, 197, 169, 243, 33, 71, 23, 228, 160, 52, 224, 150, 231, 196, 64, 76, 69, 115, 115, 108, 250, 224, 62, 252, 128, 141, 47, 210, 158, 54, 102, 30, 22, 50, 38, 198, 153, 162, 211, 148, 196, 196, 4, 9, 76, 42, 202, 27, 208, 23, 250, 53, 119, 9, 255, 36, 194, 72] }
diff --git a/proptest-regressions/internet/ip_auth_header_slice.txt b/proptest-regressions/internet/ip_auth_header_slice.txt
new file mode 100644
index 0000000..dd44c9f
--- /dev/null
+++ b/proptest-regressions/internet/ip_auth_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 e2f998d6f63804db73d82a7f750bcd3c47c81935f754dbc47d64acb87f1c00e8 # shrinks to header = IpAuthHeader { next_header: 128, spi: 0, sequence_number: 0, raw_icv: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 173, 194, 175, 193, 118, 185, 243, 139, 122, 246, 253, 120, 31, 19, 106, 172, 133, 161, 33, 155, 46, 31, 50, 82, 231, 196, 145, 146, 254, 199, 90, 48, 88, 37, 48, 190, 211, 116, 107, 4, 150, 61, 162, 89, 14, 122, 171, 194, 58, 155, 205, 154, 3, 178, 52, 214, 149, 254, 78, 192, 223, 65, 99, 101, 121, 22, 93, 236, 160, 133, 247, 103, 15, 114, 190, 37, 193, 248, 62, 16, 102, 31, 7, 180, 88, 17, 222, 108, 178, 93, 22, 240, 243, 13, 43, 255, 217, 78, 151, 146, 70, 221, 244, 88, 94, 218, 152, 86, 177, 236, 33, 0, 96, 184, 34, 245, 220, 172, 38, 208, 104, 147, 170, 58, 111, 87, 191, 112, 142, 253, 144, 15, 17, 241, 233, 242, 53, 116, 7, 6, 69, 14, 197, 155, 101, 16, 194, 206, 191, 41, 134, 123, 210, 70, 56, 110, 137, 175, 8, 123, 61, 87, 53, 40, 67, 179, 148, 228, 183, 212, 89, 127, 26, 65, 37, 7, 205, 183, 136, 213, 197, 133, 181, 226, 170, 242, 209, 79, 213, 173, 160, 85, 193, 250, 30, 146, 243, 236, 150, 9, 40, 220, 110, 104, 188, 13, 246, 75, 136, 1, 185, 158, 23, 3, 72, 199, 177, 177, 95, 195, 74, 135, 102, 40, 99, 87, 242, 176, 249, 115, 31, 140, 49, 53, 5, 42, 10, 104, 167, 46, 36, 215, 206, 33, 99, 162, 47, 223, 223, 162, 246, 207, 78, 101, 245, 201, 72, 5, 105, 231, 121, 18, 132, 252, 224, 201, 166, 148, 243, 204, 23, 102, 201, 48, 122, 141, 67, 188, 214, 235, 19, 40, 103, 138, 75, 220, 3, 31, 205, 30, 80, 195, 6, 182, 100, 239, 166, 151, 191, 141, 104, 200, 242, 250, 13, 137, 237, 15, 127, 251, 174, 164, 49, 176, 101, 201, 170, 183, 189, 15, 209, 103, 164, 141, 17, 121, 62, 27, 24, 70, 130, 174, 35, 218, 64, 216, 101, 130, 28, 125, 47, 129, 126, 199, 111, 170, 45, 21, 79, 72, 124, 59, 213, 22, 62, 160, 247, 8, 183, 2, 102, 151, 53, 36, 221, 194, 163, 93, 65, 108, 166, 202, 87, 189, 105, 95, 161, 160, 92, 113, 172, 100, 14, 220, 100, 249, 54, 255, 159, 225, 45, 13, 164, 165, 197, 119, 206, 27, 145, 1, 56, 140, 212, 134, 54, 33, 118, 16, 117, 242, 233, 143, 50, 121, 172, 30, 62, 70, 181, 196, 149, 254, 204, 11, 149, 129, 26, 156, 157, 137, 167, 222, 215, 99, 211, 178, 96, 125, 216, 45, 198, 130, 78, 47, 219, 186, 128, 69, 9, 10, 100, 247, 129, 29, 144, 48, 106, 62, 119, 214, 200, 78, 182, 54, 175, 88, 36, 203, 78, 12, 131, 254, 29, 209, 32, 197, 70, 141, 110, 12, 243, 128, 194, 36, 153, 212, 38, 165, 168, 58, 193, 56, 18, 100, 153, 245, 45, 148, 167, 83, 191, 112, 0, 114, 26, 164, 101, 184, 105, 104, 142, 247, 136, 2, 178, 71, 110, 7, 138, 240, 211, 189, 246, 119, 201, 226, 69, 162, 222, 233, 197, 40, 223, 73, 38, 9, 180, 139, 201, 23, 90, 47, 12, 178, 102, 109, 177, 150, 103, 95, 30, 99, 2, 121, 197, 231, 170, 177, 66, 211, 59, 233, 179, 82, 231, 237, 245, 139, 255, 45, 250, 203, 138, 79, 37, 87, 253, 195, 211, 235, 201, 227, 75, 159, 23, 43, 45, 87, 134, 19, 94, 53, 192, 30, 221, 201, 197] }
diff --git a/proptest-regressions/internet/ip_header.txt b/proptest-regressions/internet/ip_header.txt
new file mode 100644
index 0000000..1512bd8
--- /dev/null
+++ b/proptest-regressions/internet/ip_header.txt
@@ -0,0 +1,11 @@
+# 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 247934f91fc5186774d6d29cf6179504a97d75051e3b20dd81762f89f5cfeeba # shrinks to v4 = Ipv4Header { ihl: 6, 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: 64, header_checksum: 0, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [0, 0, 0, 0] }, v4_exts = Ipv4Extensions { auth: Some(IpAuthHeader { next_header: 45, spi: 764109140, sequence_number: 3456950140, raw_icv: [74, 82, 227, 192, 59, 221, 192, 176, 166, 69, 163, 9, 42, 86, 176, 191, 168, 242, 198, 4, 99, 179, 119, 203, 172, 72, 1, 174, 94, 3, 81, 24, 189, 84, 32, 138, 80, 227, 72, 88, 35, 243, 242, 3, 93, 49, 168, 231, 124, 89, 96, 58, 4, 239, 173, 254, 104, 56, 122, 55, 240, 183, 138, 153, 159, 217, 243, 110, 156, 83, 49, 231, 108, 179, 230, 224, 73, 246, 225, 15, 177, 61, 23, 239, 104, 132, 13, 76, 41, 42, 122, 69, 74, 81, 238, 73, 91, 31, 147, 216, 59, 201, 15, 158, 228, 51, 72, 216, 187, 197, 11, 194, 128, 218, 225, 66, 116, 83, 88, 100, 65, 222, 49, 245, 251, 152, 65, 113, 173, 167, 24, 255, 154, 222, 20, 94, 20, 89, 122, 202, 123, 123, 246, 62, 227, 76, 194, 153, 120, 39, 117, 155, 9, 29, 136, 59, 182, 186, 39, 42, 54, 30, 237, 87, 118, 132, 12, 37, 203, 225, 207, 187, 84, 117, 144, 255, 101, 121, 88, 130, 177, 185, 114, 88, 171, 2, 227, 226, 118, 129, 85, 201, 49, 65, 139, 147, 135, 129, 27, 183, 126, 74, 108, 157, 127, 12, 233, 255, 77, 69, 13, 243, 120, 42, 155, 175, 187, 105, 127, 62, 18, 191, 154, 217, 209, 102, 95, 45, 14, 251, 28, 123, 44, 60, 122, 215, 69, 180, 117, 140, 110, 23, 14, 66, 32, 126, 102, 162, 83, 103, 6, 224, 6, 74, 248, 211, 206, 178, 74, 27, 94, 85, 161, 97, 184, 88, 35, 189, 160, 172, 146, 18, 80, 133, 195, 137, 79, 78, 198, 25, 180, 174, 71, 89, 253, 230, 27, 62, 117, 166, 120, 216, 58, 50, 127, 15, 195, 198, 232, 79, 56, 108, 129, 111, 133, 138, 19, 180, 239, 24, 206, 122, 141, 107, 13, 223, 106, 79, 191, 166, 107, 119, 252, 81, 43, 153, 223, 179, 254, 187, 58, 155, 243, 133, 144, 140, 97, 145, 15, 94, 14, 244, 201, 126, 150, 200, 245, 78, 189, 146, 212, 241, 107, 47, 190, 252, 205, 2, 255, 4, 148, 109, 33, 74, 31, 144, 161, 241, 229, 100, 250, 241, 219, 99, 89, 78, 126, 129, 96, 94, 235, 120, 83, 247, 250, 9, 154, 39, 113, 138, 218, 176, 76, 67, 206, 49, 193, 78, 254, 55, 151, 180, 169, 222, 90, 155, 40, 65, 8, 222, 60, 232, 169, 76, 74, 242, 238, 9, 49, 20, 22, 184, 28, 217, 110, 177, 140, 248, 231, 241, 161, 201, 188, 242, 73, 23, 169, 11, 63, 188, 44, 15, 24, 63, 194, 225, 23, 18, 247, 4, 50, 222, 123, 2, 247, 205, 92, 94, 34, 40, 7, 177, 175, 137, 46, 126, 33, 24, 187, 235, 26, 87, 103, 252, 77, 233, 32, 33, 55, 115, 42, 173, 245, 65, 206, 181, 62, 80, 195, 100, 19, 241, 172, 41, 204, 40, 36, 192, 247, 113, 124, 228, 56, 36, 27, 20, 102, 48, 31, 144, 99, 128, 240, 184, 147, 132, 34, 85, 144, 23, 201, 106, 104, 220, 201, 13, 196, 68] }) }
+cc bec068926472dcc828c0c96f2d6d07f2ef8d61ea76fd28dd160835111b809f9b # shrinks to v4 = Ipv4Header { ihl: 6, 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: 52, header_checksum: 0, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [0, 0, 0, 0] }, v4_exts = Ipv4Extensions { auth: None }, v6 = Ipv6Header { traffic_class: 254, flow_label: 636625, payload_length: 13767, next_header: 132, hop_limit: 128, source: [124, 131, 72, 212, 91, 169, 186, 31, 59, 45, 236, 97, 44, 232, 84, 232], destination: [212, 145, 129, 163, 205, 47, 246, 109, 106, 73, 145, 35, 228, 245, 33, 4] }, v6_exts = Ipv6Extensions { hop_by_hop_options: None, destination_options: Some(Ipv6RawExtHeader { next_header: 44, payload: [132, 92, 221, 237, 135, 155, 170, 111, 248, 156, 99, 113, 235, 224, 172, 164, 244, 199, 174, 254, 42, 84, 117, 30, 99, 39, 208, 0, 150, 239, 222, 234, 32, 37, 164, 172, 145, 233, 156, 44, 247, 45, 233, 155, 222, 100, 237, 138, 4, 72, 84, 56, 111, 230, 224, 3, 130, 24, 180, 252, 65, 97, 162, 237, 116, 39, 161, 134, 173, 141, 136, 136, 0, 177, 93, 220, 132, 98, 243, 5, 218, 162, 24, 37, 192, 185, 119, 94, 192, 128, 157, 68, 102, 0, 122, 189, 255, 174, 120, 180, 48, 236, 171, 101, 155, 192, 57, 50, 107, 145, 69, 177, 231, 91, 194, 153, 12, 145, 78, 103, 23, 22, 117, 90, 14, 112, 139, 13, 183, 187, 136, 15, 186, 243, 191, 107, 164, 123, 247, 0, 81, 70, 39, 153, 5, 155, 63, 206, 190, 190, 199, 96, 111, 220, 221, 153, 52, 0, 20, 241, 73, 134, 230, 97, 231, 40, 110, 214, 48, 41, 2, 165, 193, 48, 84, 62, 109, 50, 179, 144, 199, 36, 63, 222, 196, 246, 239, 2, 4, 135, 102, 41, 84, 64, 221, 117, 94, 98, 254, 60, 62, 193, 194, 200, 146, 224, 139, 106, 99, 148, 30, 52, 236, 248, 194, 20, 118, 104, 205, 202, 57, 99, 146, 23, 201, 14, 145, 67, 55, 55, 58, 229, 187, 236, 165, 10, 101, 255, 224, 149, 160, 3, 199, 177, 231, 34, 99, 105, 89, 153, 105, 147, 111, 169, 68, 88, 131, 86, 22, 100, 87, 120, 79, 202, 222, 117, 76, 54, 136, 249, 74, 245, 139, 163, 44, 60, 116, 59, 216, 189, 38, 33, 2, 70, 4, 246, 255, 102, 77, 84, 144, 75, 191, 79, 99, 50, 133, 154, 205, 140, 199, 61, 236, 175, 71, 103, 9, 44, 245, 28, 234, 4, 12, 27, 124, 63, 206, 150, 70, 251, 43, 97, 3, 82, 226, 147, 49, 203, 68, 21, 114, 183, 185, 40, 54, 246, 16, 191, 162, 228, 51, 111, 113, 38, 98, 147, 229, 37, 64, 33, 126, 209, 139, 189, 24, 83, 26, 237, 22, 1, 81, 219, 7, 70, 97, 86, 184, 172, 200, 95, 191, 80, 108, 180, 76, 18, 85, 38, 17, 150, 106, 185, 178, 100, 114, 133, 50, 252, 144, 99, 41, 175, 96, 217, 54, 225, 34, 53, 106, 43, 168, 177, 168, 101, 145, 141, 144, 214, 17, 20, 3, 45, 116, 70, 66, 189, 223, 110, 97, 209, 148, 96, 220, 55, 91, 124, 91, 218, 109, 39, 64, 197, 85, 32, 219, 118, 55, 4, 171, 190, 100, 45, 5, 186, 247, 98, 254, 97, 235, 91, 98, 118, 245, 59, 51, 126, 239, 123, 241, 212, 87, 206, 130, 116, 21, 131, 109, 173, 50, 139, 2, 188, 12, 234, 245, 5, 123, 32, 61, 88, 186, 65, 147, 209, 232, 242, 238, 182, 143, 200, 45, 154, 24, 104, 27, 12, 171, 217, 45, 70, 174, 84, 63, 215, 200, 200, 96, 160, 54, 239, 9, 183, 196, 101, 69, 167, 169, 210, 138, 92, 190, 85, 36, 39, 187, 48, 110, 3, 220, 245, 102, 175, 121, 91, 67, 57, 105, 157, 28, 148, 37, 241, 52, 60, 79, 3, 6, 134, 209, 106, 13, 217, 205, 206, 76, 59, 198, 21, 187, 131, 203, 231, 96, 153, 217, 135, 64, 161, 140, 176, 61, 227, 191, 71, 61, 85, 96, 133, 91, 220, 67, 255, 3, 93, 235, 63, 43, 81, 219, 249] }), routing: None, fragment: Some(Ipv6FragmentHeader { next_header: 52, fragment_offset: 5515, more_fragments: true, identification: 3271804019 }), auth: None }
+cc 739aacd750e29469a31de74219980958901e73b6fc210dec6cfa7584601b5768 # shrinks to v4 = Ipv4Header { ihl: 6, 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: 68, header_checksum: 0, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [0, 0, 0, 0] }, v4_exts = Ipv4Extensions { auth: Some(IpAuthHeader { next_header: 132, spi: 1755286717, sequence_number: 64941876, raw_icv: [199, 117, 136, 37, 59, 75, 0, 128, 5, 91, 214, 114, 88, 229, 9, 123, 209, 225, 27, 203, 181, 234, 207, 203, 34, 136, 65, 38, 154, 98, 137, 91, 202, 117, 192, 146, 107, 190, 193, 5, 7, 199, 246, 123, 61, 197, 13, 232, 255, 247, 81, 83, 15, 143, 103, 111, 97, 24, 106, 143, 125, 127, 171, 80, 198, 133, 207, 62, 21, 202, 53, 176, 254, 210, 163, 200, 231, 159, 187, 122, 239, 1, 85, 158, 227, 107, 254, 60, 241, 128, 155, 115, 174, 214, 145, 30, 31, 252, 8, 58, 193, 54, 194, 130, 3, 11, 147, 69, 48, 203, 93, 82, 12, 10, 197, 207, 92, 225, 150, 194, 174, 237, 239, 15, 6, 99, 185, 106, 13, 36, 187, 186, 49, 212, 98, 138, 118, 95, 249, 115, 133, 99, 87, 90, 85, 15, 57, 20, 50, 240, 57, 201, 127, 212, 244, 89, 181, 212, 132, 62, 121, 142, 151, 176, 78, 82, 12, 200, 168, 142, 136, 168, 97, 28, 136, 237, 27, 163, 93, 28, 229, 25, 124, 203, 15, 118, 112, 124, 235, 205, 100, 164, 200, 234, 23, 66, 148, 41, 33, 191, 188, 105, 41, 223, 81, 27, 46, 44, 103, 153, 217, 61, 237, 208, 172, 79, 158, 126, 146, 53, 155, 78, 102, 52, 196, 182, 10, 180, 163, 28, 61, 43, 105, 219, 187, 204, 237, 195, 217, 112, 183, 27, 240, 140, 37, 148, 211, 247, 147, 165, 21, 35, 185, 47, 37, 91, 243, 67, 83, 98, 58, 8, 218, 234, 1, 33, 13, 193, 195, 255, 77, 78, 228, 116, 179, 208, 159, 208, 251, 230, 245, 89, 135, 144, 44, 184, 169, 212, 9, 212, 161, 50, 201, 191, 33, 51, 62, 190, 199, 166, 179, 217, 46, 242, 30, 223, 166, 88, 181, 94, 68, 25, 26, 246, 35, 105, 91, 49, 216, 100, 169, 101, 212, 66, 242, 249, 69, 182, 197, 16, 188, 18, 249, 222, 158, 148, 234, 23, 234, 227, 110, 204, 130, 104, 230, 5, 10, 72, 67, 166, 105, 225, 14, 253, 179, 128, 1, 170, 175, 94, 193, 70, 63, 111, 172, 4, 18, 111, 84, 5, 62, 85, 130, 11, 27, 127, 210, 55, 104, 120, 203, 64, 160, 161, 108, 196, 28, 14, 183, 205, 93, 21] }) }, v6 = Ipv6Header { traffic_class: 150, flow_label: 940438, payload_length: 7493, next_header: 213, hop_limit: 243, source: [196, 79, 28, 175, 25, 133, 200, 104, 240, 70, 32, 112, 181, 84, 166, 0], destination: [135, 33, 167, 240, 114, 232, 66, 187, 203, 250, 176, 72, 201, 69, 159, 190] }, v6_exts = Ipv6Extensions { hop_by_hop_options: None, destination_options: None, routing: None, fragment: Some(Ipv6FragmentHeader { next_header: 51, fragment_offset: 5306, more_fragments: false, identification: 4097407097 }), auth: Some(IpAuthHeader { next_header: 250, spi: 1017002437, sequence_number: 2730416413, raw_icv: [86, 130, 153, 31, 33, 94, 71, 46, 174, 99, 46, 121, 111, 17, 242, 34, 139, 46, 220, 130, 252, 254, 200, 15, 103, 7, 60, 149, 158, 186, 135, 42, 67, 28, 72, 211, 122, 50, 12, 247, 42, 41, 82, 202, 127, 108, 193, 24, 218, 34, 30, 23, 59, 88, 110, 206, 203, 196, 55, 25, 88, 24, 106, 185, 129, 202, 185, 154, 237, 113, 81, 18, 13, 112, 16, 245, 195, 106, 152, 43, 26, 135, 127, 254, 47, 66, 92, 18, 90, 178, 223, 14, 188, 6, 221, 98, 30, 145, 253, 243, 141, 219, 0, 45, 46, 54, 91, 3, 107, 82, 220, 160, 142, 47, 251, 7, 206, 192, 161, 192, 187, 148, 5, 114, 202, 83, 177, 170, 4, 79, 116, 32, 89, 44, 182, 195, 191, 110, 243, 110, 132, 135, 33, 62, 78, 232, 180, 233, 216, 89, 174, 156, 91, 233, 109, 77, 180, 104, 162, 52, 115, 169, 132, 213, 94, 71, 167, 218, 213, 47, 119, 199, 39, 158, 53, 153, 170, 214, 185, 206, 193, 207, 30, 0, 75, 172, 239, 203, 10, 198, 172, 253, 19, 127, 135, 24, 82, 41, 247, 235, 40, 66, 131, 97, 108, 22, 6, 250, 216, 23, 86, 213, 187, 7, 49, 162, 144, 216, 156, 101, 41, 150, 14, 25, 191, 242, 144, 139, 116, 26, 119, 107, 101, 140, 173, 83, 49, 241, 43, 62, 99, 215, 222, 3, 115, 187, 64, 205, 241, 216, 133, 66, 81, 69, 121, 205, 151, 217, 101, 191, 148, 225, 194, 253, 45, 99, 53, 113, 70, 224, 95, 255, 167, 110, 130, 193, 12, 126, 36, 200, 128, 218, 125, 22, 181, 22, 191, 254, 180, 229, 87, 214, 138, 6, 124, 237, 122, 93, 239, 67, 197, 183, 191, 203, 33, 98, 76, 201, 222, 255, 148, 179, 221, 202, 113, 61, 126, 198, 101, 158, 232, 8, 232, 35, 44, 243, 197, 189, 18, 193, 91, 39, 125, 41, 188, 88, 234, 147, 224, 57, 107, 139, 35, 117, 67, 131, 202, 157, 136, 46, 162, 48, 248, 129, 25, 112, 81, 51, 113, 159, 18, 156, 18, 165, 97, 75, 172, 85, 63, 189, 173, 75, 193, 210, 44, 15, 177, 113, 19, 4, 198, 101, 223, 249, 211, 105, 220, 201, 13, 157, 124, 230, 121, 212, 123, 107, 55, 89, 2, 144] }) }
+cc 18f6d4f0341e3a66b65724afa76b863141dfc17cc2f586d29f79b54bc29a5ba5 # shrinks to v4 = Ipv4Header { ihl: 5, 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: 0, header_checksum: 0, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [] }, v4_exts = Ipv4Extensions { auth: None }, v6 = Ipv6Header { traffic_class: 0, flow_label: 0, payload_length: 0, next_header: 23, hop_limit: 0, source: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, v6_exts = Ipv6Extensions { hop_by_hop_options: None, destination_options: Some(Ipv6RawExtHeader { next_header: 87, payload: [101, 103, 2, 106, 33, 77, 42, 59, 151, 245, 254, 50, 116, 16, 101, 193, 92, 53, 179, 110, 121, 156, 62, 20, 71, 202, 7, 94, 234, 250, 132, 131, 158, 116, 43, 244, 22, 182, 235, 67, 203, 117, 5, 50, 50, 249, 226, 163, 218, 50, 153, 142, 87, 208, 210, 69, 194, 86, 110, 115, 205, 8, 251, 53, 56, 213, 123, 61, 69, 13, 114, 225, 69, 8, 236, 140, 154, 208, 38, 15, 238, 24, 116, 95, 2, 64, 105, 159, 180, 114, 41, 116, 159, 182, 135, 106, 32, 164, 127, 202, 58, 25, 125, 104, 162, 159, 54, 145, 106, 81, 146, 75, 91, 42, 96, 20, 85, 106, 173, 225, 20, 231, 127, 58, 25, 218, 93, 24, 0, 129, 176, 123, 249, 27, 174, 123, 84, 167, 112, 90, 251, 183, 161, 6, 150, 64, 250, 149, 132, 247, 247, 158, 54, 177, 190, 17, 205, 187, 153, 222, 121, 89, 36, 34, 84, 83, 89, 60, 102, 200, 227, 252, 207, 55, 6, 104, 235, 13, 222, 216, 126, 89, 97, 140, 191, 130, 231, 217, 14, 63, 161, 254, 227, 179, 67, 4, 196, 106, 149, 64, 116, 132, 22, 155, 69, 25, 43, 219, 231, 203, 202, 243, 24, 233, 44, 214, 137, 102, 60, 66, 187, 254, 66, 172, 200, 7, 85, 70, 168, 232, 18, 228, 195, 110, 52, 199, 24, 208, 214, 57, 217, 155, 17, 35, 132, 238, 116, 21, 247, 157, 101, 137, 213, 28, 82, 162, 205, 127, 249, 234, 132, 130, 38, 98, 182, 81, 85, 201, 33, 203, 204, 194, 209, 121, 39, 250, 183, 118, 44, 146, 159, 100, 241, 161, 148, 148, 54, 103, 179, 123, 151, 172, 148, 39, 228, 26, 176, 237, 251, 236, 23, 156, 158, 104, 227, 64, 196, 204, 108, 192, 189, 39, 37, 133, 28, 215, 137, 216, 140, 167, 232, 157, 204, 50, 250, 6, 14, 157, 184, 18, 81, 111, 229, 101, 48, 140, 79, 192, 152, 127, 94, 123, 131, 124, 47, 48, 187, 23, 73, 180, 207, 10, 130, 177, 170, 220, 228, 213, 50, 185, 102, 151, 21, 22, 215, 179, 152, 88, 37, 26, 233, 38, 160, 2, 130, 81, 137, 159, 61, 163, 221, 40, 123, 70, 170, 206, 15, 143, 162, 171, 126, 67, 104, 55, 212, 229, 225, 195, 231, 38, 221, 194, 39, 154, 196, 63, 94, 179, 74, 139, 6, 186, 254, 81, 239, 15, 243, 118, 77, 235, 21, 248, 19, 131, 101, 12, 158, 250, 206, 233, 222, 113, 22, 58, 26, 205, 21, 245, 207, 113, 109, 165, 134, 181, 68, 119, 13, 254, 162, 37, 11, 148, 227, 246, 105, 112, 134, 65, 220, 112, 116, 109, 66, 153, 249, 55, 5, 148, 185, 92, 49, 0, 111, 75, 44, 106, 72, 173, 124, 242, 51, 232, 215, 182, 167, 209, 123, 195, 163, 32, 93, 249, 216, 247, 91, 147, 246, 123, 96, 166, 118, 81, 99, 220, 113, 9, 1, 235, 240, 189, 114, 143, 105, 6, 195, 253, 102, 5, 181, 31, 223, 147, 118, 254, 17, 252, 150, 139, 86, 252, 200, 254, 27, 100, 130, 53, 167, 46, 70, 255, 98, 247, 26, 219, 125, 255, 190, 218, 225, 223, 148, 71, 183, 167, 50, 235, 111, 71, 152, 55, 96, 92, 109, 105, 111, 127, 51, 151, 36, 15, 222, 236, 235, 63, 45, 37, 202, 80, 92, 65, 142, 166, 137, 32, 7, 81, 8, 164, 106, 83, 253, 239, 136, 135, 227, 79, 110, 38, 242, 159, 44, 16, 97, 110, 196, 134, 229, 241, 43, 192, 48, 84, 8, 91, 237, 207, 152, 62, 121, 169, 255, 58, 5, 102, 138, 253, 54, 115, 71, 141, 44, 5, 227, 75, 0, 26, 0, 99, 165, 182, 233, 146, 28, 210, 49, 9, 202, 124, 21, 251, 60, 247, 26, 190, 138, 119, 59, 13, 182, 204, 149, 75, 135, 161, 33, 3, 41, 139, 70, 150, 185, 194, 217, 106, 1, 51, 227, 111, 160, 71, 100, 7, 187, 89, 31, 75, 151, 194, 192, 50, 209, 190, 34, 104, 63, 145, 230, 19, 8, 59, 145, 35, 32, 35, 54, 6, 1, 31, 192, 109, 48, 249, 48, 171, 193, 109, 184, 176, 178, 57, 101, 172, 132, 13, 29, 82, 16, 118, 108, 97, 216, 147, 160, 6, 211, 46, 235, 25, 234, 100, 157, 22, 218, 28, 91, 175, 34, 85, 37, 152, 41, 176, 89, 62, 138, 33, 43, 16, 184, 71, 81, 249, 96, 69, 102, 176, 250, 111, 43, 222, 23, 65, 200, 20, 172, 103, 249, 134, 209, 47, 178, 127, 136, 211, 155, 0, 184, 100, 46, 183] }), routing: None, fragment: None, auth: None }
+cc 1f2acfdaad9440867bcff5cbab6e7dc6fe252f7fccc307d9b0a429d0f22c643d # shrinks to v4 = Ipv4Header { dscp: Ipv4Dscp(12), ecn: Ipv4Ecn(0), total_len: 53473, identification: 14386, dont_fragment: true, more_fragments: false, fragment_offset: IpFragOffset(7848), time_to_live: 72, protocol: 215, header_checksum: 26819, source: [0, 0, 0, 0], destination: [0, 0, 0, 93], options: [] }, v4_exts = Ipv4Extensions { auth: Some(IpAuthHeader { next_header: 152, spi: 3666043737, sequence_number: 2600127895, raw_icv: [159, 116, 11, 27, 133, 164, 56, 166, 62, 191, 237, 14, 27, 135, 96, 80, 134, 197, 165, 36, 40, 160, 237, 136, 125, 5, 146, 40, 185, 130, 55, 92, 50, 162, 194, 238, 138, 12, 55, 209, 203, 182, 200, 23, 165, 98, 174, 50, 116, 18, 142, 13, 189, 238, 179, 50, 166, 106, 32, 98, 235, 14, 150, 186, 126, 182, 27, 62, 145, 146, 184, 192, 162, 149, 201, 251, 250, 99, 211, 68, 221, 255, 154, 229, 215, 145, 150, 226, 224, 177, 204, 240, 218, 20, 11, 81, 163, 146, 119, 156, 116, 202, 204, 7, 174, 3, 101, 89, 153, 159, 46, 86, 162, 94, 144, 181, 129, 148, 53, 131, 114, 239, 27, 224, 2, 253, 213, 110, 140, 177, 194, 237, 139, 31, 60, 187, 147, 139, 175, 17, 240, 19, 141, 64, 251, 78, 162, 40, 48, 231, 95, 195, 109, 201, 9, 244, 194, 243, 25, 76, 14, 209, 84, 57, 29, 204, 223, 162, 19, 225, 231, 158, 101, 60, 92, 11, 199, 163, 12, 166, 160, 55, 42, 99, 92, 209, 49, 159, 30, 217, 239, 105, 73, 120, 145, 155, 112, 92, 243, 246, 25, 4, 128, 127, 178, 168, 135, 195, 122, 184, 35, 102, 144, 241, 131, 48, 113, 247, 251, 45, 142, 110, 51, 62, 108, 200, 41, 155, 21, 217, 239, 144, 139, 119, 110, 69, 193, 203, 115, 30, 173, 214, 169, 188, 70, 208, 109, 128, 240, 6, 116, 216, 57, 141, 85, 227, 140, 82, 99, 79, 163, 147, 102, 127, 247, 117, 135, 246, 130, 103, 188, 227, 240, 128, 201, 159, 77, 83, 247, 2, 224, 158, 118, 158, 229, 48, 206, 217, 46, 102, 254, 155, 84, 87, 13, 181, 40, 99, 218, 205, 41, 249, 246, 193, 128, 178, 74, 73, 126, 103, 207, 237, 183, 113, 219, 114, 162, 106, 101, 17, 219, 111, 102, 34, 68, 113, 105, 215, 57, 152, 178, 216, 254, 214, 94, 182, 31, 239, 222, 69, 147, 18, 94, 34, 71, 221, 151, 112, 17, 96, 71, 3, 244, 253, 60, 182, 49, 90, 205, 109, 113, 113, 163, 23, 50, 41, 249, 208, 20, 212, 163, 237, 71, 121, 91, 197, 20, 163, 83, 207, 160, 1, 1, 57, 100, 139, 132, 55, 218, 120, 24, 202, 134, 241, 142, 200, 164, 189, 162, 150, 78, 231, 69, 39, 127, 206, 185, 48, 247, 105, 32, 74, 41, 132, 117, 47, 52, 116, 52, 70, 232, 155, 98, 240, 83, 252, 181, 164, 165, 26, 155, 194, 175, 90, 58, 20, 201, 98, 191, 36, 185, 28, 179, 108, 249, 78, 222, 198, 119, 194, 140, 212, 116, 37, 180, 81, 130, 202, 46, 184, 104, 200, 151, 81, 102, 32, 90, 184, 202, 175, 131, 227, 16, 110, 36, 19, 235, 151, 236, 58, 4, 102, 136, 128, 196, 198, 225, 78, 213, 198, 254, 53, 42, 132, 177, 21, 195, 52, 73, 196, 1, 61, 171, 168, 111, 78, 68, 35, 228, 43, 220, 143, 118, 73, 218, 209, 74, 40, 32, 217, 97, 74, 8, 117, 146, 184, 9, 177, 49, 227, 84, 193, 180, 163, 44, 198, 40, 167, 40, 51, 255, 40, 126, 58, 213, 17, 236, 254, 45, 124, 199, 89, 226, 109, 222, 238, 175, 76, 132, 189, 251, 135, 53, 145, 178, 240, 25, 32, 105, 33, 58, 197, 7, 106, 69, 194, 146, 67, 130, 99, 252, 165, 235, 117, 34, 231, 122, 224, 139, 201, 10, 126, 188, 210, 225, 44, 228, 96, 161, 129, 142, 88, 139, 230, 224, 176, 53, 133, 143, 195, 235, 119, 152, 181, 157, 94, 132, 201, 64, 79, 123, 113, 62, 197, 108, 126, 174, 36, 235, 246, 110, 213, 116, 111, 113, 4, 65, 105, 87, 85, 22, 53, 169, 210, 146, 181, 167, 96, 233, 164, 104, 115, 191, 61, 186, 93, 180, 230, 156, 188, 10, 73, 176, 178, 173, 137, 93, 40, 45, 218, 214, 141, 211, 246, 117, 14, 52, 106, 195, 105, 165, 148, 232, 8, 76, 9, 133, 230, 184, 130, 13, 244, 54, 53, 154, 88, 150, 240, 222, 203, 122, 163, 206, 217, 146, 67, 88, 88, 30, 223, 224, 37, 159, 153, 96, 147, 164, 217, 195, 23, 82, 111, 82, 170, 248, 255, 16, 10, 15, 7, 169, 103, 179, 223, 109, 40, 234, 167, 122, 16, 98, 203, 32, 86, 124, 170, 189, 12, 153, 62, 227, 106, 31, 182, 88, 192, 177, 2, 91, 63, 255, 102, 197, 182, 146, 149, 134, 9, 88, 168, 68, 205, 26, 9, 78, 217, 6, 17, 30, 137, 229, 129, 200, 174, 99, 103, 2, 188, 174, 111, 81, 15, 151, 188, 139, 51, 27, 206, 178, 139, 245, 17, 253, 7, 128, 61, 218, 231, 19, 127, 74, 98, 125, 168, 114, 163, 218, 153, 168, 19, 7, 73, 31, 48, 33, 243, 51, 210, 240, 112, 117, 122, 67, 99, 169, 218, 245, 127, 241, 165, 8, 173, 103, 108, 223, 217, 173, 236, 134, 73, 208, 142, 199, 18, 92, 242, 31, 176, 131, 68, 18, 153, 35, 187, 236, 170, 33, 54, 105, 5, 135, 96, 15, 255, 149, 207, 121, 222, 78, 108] }) }, v6 = Ipv6Header { traffic_class: 225, flow_label: Ipv6FlowLabel(972970), payload_length: 53892, next_header: 200, hop_limit: 25, source: [147, 222, 99, 138, 39, 104, 135, 9, 175, 147, 38, 24, 174, 77, 32, 11], destination: [139, 96, 40, 233, 130, 114, 128, 250, 193, 218, 138, 136, 96, 225, 36, 140] }, v6_exts = Ipv6Extensions { hop_by_hop_options: None, destination_options: None, routing: Some(Ipv6RoutingExtensions { routing: Ipv6RawExtHeader { next_header: 44 (IPv6-Frag - Fragment Header for IPv6), payload: [227, 99, 203, 200, 80, 3, 131, 72, 240, 251, 48, 7, 160, 116, 126, 249, 60, 65, 126, 160, 195, 165, 212, 160, 186, 243, 29, 28, 67, 215, 149, 205, 203, 3, 59, 234, 137, 65, 91, 240, 24, 223, 167, 253, 6, 191, 152, 121, 240, 38, 252, 30, 155, 154, 66, 247, 54, 158, 210, 118, 105, 83, 161, 166, 187, 155, 240, 225, 178, 6, 222, 123, 217, 105, 95, 123, 23, 60, 51, 185, 10, 156, 15, 193, 105, 174, 114, 185, 132, 85, 31, 53, 168, 89, 198, 76, 237, 244, 4, 108, 91, 33, 251, 135, 141, 134, 167, 17, 104, 0, 244, 211, 39, 27, 160, 197, 39, 94, 123, 229, 28, 46, 245, 67, 67, 250, 9, 4, 56, 251, 37, 45, 63, 209, 0, 76, 125, 141, 72, 65, 198, 64, 81, 184, 121, 96, 125, 131, 67, 239, 222, 149, 31, 81, 103, 171, 91, 204, 14, 152, 151, 34, 235, 60, 107, 59, 55, 233, 234, 29, 19, 117, 146, 130, 153, 175, 162, 63, 29, 94, 100, 21, 177, 227, 66, 94, 230, 11, 169, 171, 168, 79, 13, 86, 147, 17, 42, 116, 153, 220, 159, 71, 111, 104, 247, 35, 196, 23, 138, 119, 104, 27, 9, 230, 223, 213, 97, 16, 81, 65, 110, 239, 45, 195, 73, 54, 76, 232, 98, 95, 78, 154, 78, 20, 96, 24, 192, 62, 124, 40, 141, 200, 224, 173, 229, 198, 85, 88, 83, 136, 38, 96, 244, 211, 120, 41, 157, 138, 227, 150, 191, 158, 138, 132, 242, 168, 224, 80, 70, 180, 59, 106, 244, 145, 215, 100, 66, 228, 56, 197, 67, 85, 120, 51, 182, 98, 188, 84, 161, 111, 65, 111, 237, 38, 20, 237, 240, 17, 21, 47, 107, 128, 255, 22, 114, 13, 205, 1, 159, 37, 131, 187, 47, 162, 49, 215, 47, 209, 234, 17, 187, 160, 54, 2, 50, 11, 56, 138, 31, 61, 236, 245, 144, 102, 149, 34, 252, 117, 169, 8, 181, 219, 154, 233, 52, 131, 252, 161, 144, 152, 247, 175, 230, 75, 61, 228, 15, 111, 100, 52, 220, 235, 97, 41, 81, 137, 229, 46, 22, 159, 244, 190, 156, 165, 125, 242, 45, 188, 20, 80, 72, 126, 242, 238, 44, 18, 138, 194, 52, 239, 82, 40, 212, 75, 142, 181, 218, 83, 84, 77, 206, 172, 125, 146, 59, 165, 79, 182, 117, 79, 100, 189, 186, 221, 36, 22, 11, 227, 11, 83, 190, 8, 78, 191, 199, 81, 168, 253, 26, 185, 152, 153, 16, 133, 194, 216, 226, 101, 55, 87, 67, 202, 198, 166, 68, 233, 100, 56, 129, 171, 204, 121, 174, 232, 5, 41, 199, 238, 8, 209, 66, 136, 183, 46, 248, 142, 97, 244, 111, 103, 28, 175, 66, 255, 211, 207, 234, 231, 98, 46, 227, 44, 15, 168, 124, 245, 20, 70, 108, 27, 171, 140, 10, 28, 248, 247, 27, 136, 77, 158, 135, 161, 103, 106, 131, 103, 42, 50, 70, 6, 166, 242, 116, 16, 107, 94, 136, 215, 90, 218, 90, 37, 246, 182, 199, 17, 16, 58, 212, 8, 117, 78, 56, 187, 6, 228, 92, 124, 223, 1, 253, 173, 176, 54, 25, 30, 175, 39, 206, 53, 59, 64, 183, 48, 213, 0, 150, 173, 123, 144, 92, 115, 145, 32, 147, 146, 222, 98, 245, 255, 15, 52, 17, 250, 212, 27, 10, 243, 184, 55, 38, 0, 135, 99, 0, 135, 41, 72, 75, 34] }, final_destination_options: Some(Ipv6RawExtHeader { next_header: 158, payload: [226, 98, 140, 190, 5, 139, 3, 216, 82, 169, 206, 130, 138, 255, 211, 136, 203, 15, 216, 4, 175, 38, 104, 201, 114, 10, 150, 3, 212, 212, 146, 14, 247, 231, 200, 89, 124, 237, 23, 152, 121, 106, 118, 202, 39, 88, 242, 176, 112, 159, 133, 71, 73, 182, 102, 45, 15, 112, 38, 95, 86, 69, 244, 117, 116, 115, 33, 19, 12, 144, 208, 167, 244, 15, 207, 99, 19, 35, 216, 218, 3, 15, 192, 200, 130, 105, 117, 28, 99, 237, 22, 131, 63, 36, 188, 128, 215, 21, 161, 211, 255, 155, 228, 99, 1, 25, 41, 209, 123, 188, 66, 209, 216, 32, 0, 98, 208, 125, 84, 162, 158, 61, 75, 213, 10, 118, 3, 205, 8, 197, 217, 98, 82, 186, 184, 227, 126, 88, 23, 205, 145, 234, 135, 119, 155, 153, 96, 135, 100, 27, 212, 207, 182, 236, 185, 86, 223, 123, 202, 62, 47, 55, 252, 94, 89, 28, 64, 198, 123, 212, 108, 165, 190, 102, 194, 26, 214, 150, 24, 222, 179, 129, 209, 31, 151, 104, 158, 53, 117, 252, 105, 11, 214, 244, 184, 243, 129, 220, 226, 182, 180, 211, 69, 246, 212, 145, 3, 157, 206, 8, 49, 67, 64, 177, 205, 119, 58, 242, 140, 26, 34, 10, 159, 209, 224, 49, 248, 43, 173, 110, 148, 67, 171, 6, 170, 23, 87, 105, 100, 18, 217, 21, 197, 136, 215, 90, 127, 127, 9, 113, 31, 80, 153, 159, 67, 86, 147, 131, 45, 84, 247, 29, 11, 210, 194, 232, 52, 97, 15, 248, 120, 92, 195, 237, 213, 86, 237, 90, 182, 29, 42, 198, 243, 13, 230, 11, 7, 138, 15, 230, 201, 40, 144, 176, 78, 178, 144, 85, 35, 40, 239, 16, 122, 8, 108, 88, 3, 244, 17, 144, 120, 228, 72, 248, 158, 78, 116, 225, 16, 168, 124, 78, 149, 137, 212, 92, 55, 206, 107, 27, 67, 70, 53, 215, 66, 130, 90, 27, 41, 138, 94, 113, 136, 216, 31, 218, 34, 209, 188, 125, 66, 10, 82, 157, 183, 61, 196, 65, 69, 163, 136, 160, 85, 121, 46, 12, 100, 128, 6, 165, 63, 129, 149, 157, 56, 239, 11, 2, 14, 44, 41, 197, 206, 174, 17, 147, 153, 138, 198, 76, 232, 158, 233, 44, 10, 233, 52, 29, 196, 240, 25, 172, 169, 237, 233, 114, 161, 184, 91, 140, 70, 168, 125, 51, 246, 88, 234, 58, 173, 126, 149, 177, 193, 113, 43, 25, 36, 94, 164, 108, 107, 36, 78, 29, 79, 87, 29, 218, 161, 2, 157, 225, 84, 67, 193, 142, 255, 79, 249, 104, 116, 223, 197, 190, 246, 192, 171, 220, 165, 24, 29, 135, 75, 184, 130, 25, 47, 66, 10, 171, 253, 111, 189, 39, 4, 18, 20, 192, 63, 130, 87, 142, 224, 19, 19, 53, 241, 212, 62, 38, 12, 252, 225, 0, 16, 255, 102, 64, 226, 59, 174, 239, 59, 27, 210, 206, 209, 77, 140, 152, 188, 48, 240, 8, 3, 185, 212, 37, 122, 146, 115, 219, 66, 126, 238, 104, 126, 98, 67, 159, 57, 158, 171, 213, 156, 190, 155, 123, 229, 14, 217, 175, 107, 59, 240, 122, 133, 252, 132, 114, 182, 204, 201, 193, 44, 33, 51, 0, 184, 153, 88, 140, 139, 239, 119, 53, 205, 142, 103, 124, 68, 60, 243, 39, 148, 164, 248, 87, 246, 113, 95, 145, 113, 38, 8, 147, 27, 253, 255, 17, 60, 68, 72, 44, 60, 9, 200, 247, 174, 146, 187, 86, 244, 171, 13, 35, 141, 179, 107, 58, 53, 158, 76, 207, 118, 78, 251, 68, 200, 235, 51, 127, 224, 226, 188, 178, 24, 230, 240, 204, 88, 113, 186, 70, 113, 18, 139, 135, 222, 248, 102, 116, 49, 62, 229, 212, 47, 181, 151, 104, 236, 54, 171, 33, 118, 221, 43, 232, 21, 225, 110, 140, 188, 131, 211, 223, 158, 128, 185, 24, 84, 217, 181, 215, 39, 203, 82, 159, 109, 202, 160, 59, 211, 243, 127, 66, 220, 10, 141, 236, 58, 184, 211, 64, 132, 144, 156, 154, 221, 242, 2, 148, 34, 40, 86, 212, 185, 90, 55, 208, 153, 121, 245, 226, 173, 141, 142, 11, 199, 95, 97, 69, 158, 3, 18, 223, 4, 80, 139, 247, 199, 173, 22, 50, 117, 253, 160, 189, 68, 215, 5, 195, 208, 114, 150, 12, 131, 218, 168, 74, 47, 40, 11, 164, 62, 92, 197, 231, 206, 75, 17, 52, 75, 122, 168, 104, 236, 31, 119, 138, 24, 158, 106, 109, 204, 193, 152, 158, 18, 158, 167, 23, 242, 140, 74, 217, 94, 91, 44, 181, 61, 126, 22, 203, 185, 172, 51, 21, 153, 167, 250, 221, 213, 170, 224, 160, 4, 108, 195, 197, 60, 100, 239, 180, 55, 57, 32, 209, 195, 212, 230, 62, 106, 116, 176, 180, 162, 79, 30, 158, 55, 255, 99, 45, 243, 154, 79, 159, 251, 241, 39, 220, 111, 248, 46, 119, 44, 12, 152, 111, 86, 101, 34, 245, 190, 130, 157, 150, 145, 145, 73, 165, 200, 154, 152, 13, 151, 66, 64, 1, 208, 204, 41, 253, 58, 139, 80, 250, 204, 116, 152, 182, 34, 122, 82, 177, 231, 233, 134, 64, 12, 114, 196, 23, 138, 176, 200, 147, 214, 254, 181, 118, 156, 255, 205, 198, 166, 66, 107, 29, 192, 96, 4, 112, 86, 126, 156, 114, 54, 248, 45, 157, 113, 63, 241, 33, 145, 224, 45, 54, 220, 120, 92, 195, 92, 147, 170, 188, 247, 220, 117, 115, 230, 182, 220, 242, 81, 89, 192, 162, 223, 253, 163, 88, 251, 28, 208, 238, 52, 212, 215, 219, 220, 67, 194, 99, 241, 87, 44, 233, 222, 35, 37, 163, 119, 133, 21, 176, 96, 214, 97, 229, 198, 48, 155, 100, 165, 136, 227, 68, 39, 164, 185, 254, 130, 244, 143, 204, 97, 213, 251, 153, 125, 200, 97, 40, 215, 212, 225, 199, 113, 116, 192, 40, 229, 103, 153, 46, 101, 23, 38, 239, 174, 213, 110, 14, 138, 38, 166, 59, 75, 44, 241, 249, 80, 142, 151, 13, 126, 233, 158, 157, 24, 68, 130, 250, 28, 35, 223, 210, 247, 158, 122, 244, 158, 81, 25, 99, 68, 245, 232, 115, 64, 26, 49, 112, 110, 192, 148, 195, 85, 193, 199, 60, 185, 125, 145, 208, 150, 128, 134, 108, 175, 176, 6, 25, 139, 66, 251, 254, 100, 20, 163, 211, 195, 80, 249, 185, 94, 4, 39, 10, 154, 39, 114, 217, 130, 38, 145, 210, 225, 37, 132, 110, 184, 180, 242, 239, 176, 35, 74, 83, 208, 52, 130, 195, 66, 6, 102, 158, 227, 103, 29, 240, 210, 71, 172, 165, 11, 151, 161, 91, 33, 92, 93, 20, 24, 125, 146, 113, 146, 156, 174, 47, 15, 117, 103, 86, 71, 170, 132, 138, 55, 196, 141, 147, 250, 101, 50, 75, 135, 50, 41, 73, 168, 138, 69, 220, 249, 54, 164, 15, 73, 210, 11, 200, 177, 107, 124, 19, 7, 69, 52, 150, 122, 180, 107, 218, 24, 214, 12, 86, 26, 165, 121, 151, 197, 255, 198, 242, 24, 218, 39, 59, 231, 35, 225, 155, 23, 157, 41, 2, 119, 175, 143, 35, 132, 52, 172, 17, 202, 108, 101, 37, 79, 19, 114, 189, 164, 214, 76, 101, 66, 205, 145, 180, 79, 142, 3, 250, 7, 120, 24, 3, 69, 20, 75, 91, 15, 157, 53, 238, 0, 34, 45, 13, 228, 146, 214, 219, 89, 82, 213, 168, 158, 155, 233, 202, 45, 96, 9, 95, 125, 70, 31, 172, 109, 128, 40, 195, 37, 200, 159, 255, 36, 234, 30, 143, 209, 173, 151, 140, 242, 98, 238, 82, 145, 151, 18, 95, 180, 65, 254, 175, 97, 131, 92, 254, 155, 15, 154, 82, 214, 247, 121, 235, 121, 215, 48, 230, 10, 235, 228, 154, 61, 103, 51, 200, 12, 184, 117, 7, 239, 76, 121, 235, 190, 234, 173, 17, 96, 4, 125, 41, 73, 236, 56, 155, 28, 163, 170, 41, 120, 39, 23, 73, 14, 70, 155, 16, 187, 81, 105, 62, 62, 56, 143, 51, 93, 167, 48, 53, 236, 219, 96, 82, 54, 145, 130, 238, 40, 141, 138, 83, 13, 181, 27, 168, 118, 85, 162, 116, 222, 13, 189, 182, 239, 199, 190, 192, 231, 133, 20, 40, 50, 118, 154, 90, 110, 103, 108, 83, 51, 28, 41, 8, 123, 174, 25, 80, 238, 118, 55, 78, 11, 91, 207, 121, 200, 154, 160, 157, 77, 117, 221, 209, 59, 26, 215, 35, 165, 70, 225, 103, 234, 87, 132, 165, 98, 231, 49, 13, 6, 206, 208, 181, 22, 199, 245, 171, 143, 239, 228, 145, 95, 210, 12, 68, 94, 60, 121, 197, 134, 142, 70, 165, 229, 69, 42, 145, 99, 211, 86, 185, 107, 166, 240, 180, 126, 120, 55, 147, 115, 89, 31, 226, 124, 180, 73, 29, 34, 112, 29, 215, 219, 245, 169, 73, 105, 161, 88, 218, 192, 116, 209, 180, 134, 50, 59, 40, 66, 130, 24, 79, 183, 125, 76, 49, 11, 15, 219, 166, 128, 123, 111, 153, 154, 20, 162, 15, 170, 49, 45, 235, 115, 18, 124, 28, 179, 37, 82, 108, 244, 11, 254, 253, 211, 235, 148, 189, 156, 65, 136, 238, 9, 67, 96, 210, 185, 163, 19, 108, 254, 251, 148, 87, 180, 187, 114, 58, 186, 100, 128, 254, 183, 83, 109, 19, 10, 14, 156, 235, 193, 250, 74, 164, 124, 89, 42, 201, 100, 140, 238, 222, 16, 43, 114, 214, 113, 3, 37, 69, 142, 187, 88, 132, 155, 53, 137, 242, 120, 158, 210, 97, 176, 20, 193, 170, 14, 227, 7, 155, 78, 197, 152, 68, 226, 199, 107, 86, 182, 141, 6, 16, 146, 186, 101, 243, 99, 110, 171, 156, 53, 150, 136, 104, 210, 160, 159, 82, 106, 40, 79, 39, 177, 185, 152, 184, 98, 94, 166, 127, 83, 42, 239, 155, 76, 149, 222, 219, 169, 209, 231, 164, 48, 95, 184, 127, 99, 10, 193, 42, 217, 112, 202, 102, 0, 111, 222, 127, 74, 250, 103, 129, 43, 153, 43, 133, 158, 153, 90, 11, 174, 91, 204, 52, 230, 227, 104, 191, 206, 222, 237, 86, 70, 233, 94, 14, 197, 82, 206, 216, 134, 17, 205, 250, 201, 65, 187, 221, 129, 38, 138, 98, 190, 166, 68, 185, 106, 177, 148, 157, 118, 154, 183, 178, 249, 62, 107, 91, 7, 228, 113, 53, 112, 67, 11, 158, 228, 118, 101, 67, 222, 179, 40, 95, 137, 190, 144, 211, 232, 54, 223, 167, 107, 109, 191, 144, 4, 237, 175, 234, 82, 4, 159, 88, 231, 73, 111, 181, 203, 221, 252, 111, 135, 194, 25, 223, 178, 71, 88, 31, 115, 159, 194, 95, 250, 103, 159, 245, 101, 18, 238, 248, 232, 162, 239, 17, 190, 94, 219, 106, 126, 97, 27, 210, 47, 82, 38, 235, 36, 144, 106, 187, 205, 0, 23, 33, 107, 19, 147, 182, 152, 61, 150, 246, 10, 175, 99, 156, 146, 218, 72, 48, 63, 206, 189, 113] }) }), fragment: Some(Ipv6FragmentHeader { next_header: 51 (AH - Authentication Header), fragment_offset: IpFragOffset(344), more_fragments: true, identification: 3962516666 }), auth: Some(IpAuthHeader { next_header: 60 (IPv6-Opts - Destination Options for IPv6), spi: 736038927, sequence_number: 2462865164, raw_icv: [199, 198, 91, 91, 182, 22, 42, 127, 26, 205, 203, 86, 166, 62, 56, 2, 238, 116, 26, 115, 222, 60, 230, 165, 248, 145, 131, 31, 38, 0, 116, 227, 132, 242, 141, 46, 240, 93, 67, 218, 85, 0, 145, 175, 232, 194, 249, 181, 44, 85, 26, 106, 29, 199, 64, 187, 75, 2, 157, 231, 146, 76, 170, 233, 166, 229, 136, 123, 140, 238, 14, 30, 18, 169, 96, 230, 35, 177, 54, 60, 184, 121, 137, 210, 171, 254, 118, 237, 119, 105, 156, 146, 151, 117, 191, 38, 73, 248, 101, 36, 122, 14, 89, 14, 44, 91, 255, 211, 216, 182, 243, 235, 193, 70, 50, 180, 174, 4, 58, 139, 205, 184, 148, 122, 38, 199, 222, 39, 187, 183, 107, 5, 44, 186, 217, 155, 226, 2, 200, 81, 65, 149, 151, 249, 195, 179, 0, 68, 118, 255, 36, 181, 239, 108, 202, 0, 51, 32, 180, 193, 160, 214, 104, 160, 155, 116, 159, 229, 129, 75, 192, 53, 252, 79, 169, 194, 180, 34, 74, 131, 192, 217, 14, 251, 32, 73, 170, 190, 127, 136, 172, 173, 162, 245, 67, 13, 85, 15, 81, 197, 6, 70, 83, 244, 249, 1, 56, 13, 191, 75, 158, 172, 40, 58, 33, 125, 49, 62, 22, 135, 122, 219, 225, 110, 109, 20, 128, 80, 83, 220, 6, 0, 206, 73, 237, 6, 96, 219, 93, 240, 70, 149, 109, 244, 217, 17, 213, 69, 200, 75, 20, 20, 71, 222, 181, 69, 182, 206, 66, 162, 54, 98, 72, 235, 190, 169, 123, 183, 95, 47, 127, 56, 202, 236, 182, 153, 79, 198, 158, 119, 31, 33, 143, 154, 129, 251, 190, 57, 148, 154, 120, 253, 122, 222, 109, 170, 98, 30, 181, 95, 247, 230, 88, 141, 5, 158, 23, 253, 5, 65, 220, 22, 58, 62, 214, 103, 54, 75, 62, 110, 161, 146, 58, 48, 51, 165, 139, 123, 112, 50, 234, 153, 98, 79, 54, 183, 95, 190, 231, 255, 94, 199, 208, 184, 173, 60, 221, 237, 191, 80, 180, 160, 105, 17, 246, 32, 34, 203, 95, 104, 248, 196, 205, 254, 39, 196, 115, 29, 176, 78, 67, 225, 231, 204, 99, 206, 162, 135, 130, 94, 240, 188, 25, 203, 115, 1, 54, 190, 119, 62, 31, 41, 97, 22, 139, 109, 172, 86, 58, 0, 61, 106, 254, 29, 21, 53, 201, 80, 122, 9, 237, 217, 225, 228, 1, 66, 89, 235, 164, 93, 50, 234, 147, 11, 100, 48, 69, 175, 187, 246, 85, 64, 210, 80, 164, 159, 136, 198, 67, 33, 119, 2, 225, 130, 22, 176, 206, 251, 41, 217, 59, 139, 241, 136, 62, 0, 237, 38, 199, 102, 205, 141, 235, 181, 181, 31, 182, 252, 99, 108, 198, 7, 34, 73, 238, 64, 163, 96, 185, 25, 21, 192, 236, 150, 12, 139, 122, 234, 87, 75, 106, 236, 139, 137, 239, 197, 186, 228, 34, 193, 170, 40, 242, 176, 51, 174, 63, 218, 148, 240, 37, 24, 76, 100, 130, 16, 130, 81, 2, 169, 56, 20, 173, 210, 88, 210, 234, 158, 219, 185, 233, 127, 105, 238, 153, 246, 98, 204, 88, 58, 29, 48, 130, 53, 29, 232, 110, 68, 237, 184, 167, 182, 26, 106, 140, 199, 54, 244, 19, 99, 225, 147, 71, 56, 193, 254, 167, 191, 251, 66, 99, 255, 44, 139, 229, 132, 219, 57, 8, 134, 223, 216, 154, 134, 111, 146, 252, 173, 180, 136, 82, 158, 90, 29, 134, 155, 207, 171, 81, 106, 62, 234, 13, 139, 44, 19, 201, 177, 99, 119, 0, 88, 175, 164, 88, 231, 164, 65, 131, 189, 221, 188, 227, 22, 20, 203, 248, 229, 15, 101, 227, 110, 178, 150, 104, 226, 61, 165, 50, 237, 254, 130, 194, 166, 166, 5, 185, 93, 88, 21, 91, 248, 220, 195, 215, 69, 210, 158, 49, 123, 156, 157, 11, 218, 2, 75, 159, 196, 192, 184, 171, 174, 126, 35, 240, 77, 104, 165, 108, 90, 236, 154, 109, 168, 110, 158, 16, 105, 250, 0, 255, 55, 217, 88, 249, 94, 34, 102, 178, 176, 147, 209, 226, 118, 29, 80, 129, 119, 122, 145, 235, 116, 130, 230, 211, 63, 129, 205, 73, 28, 236, 29, 23, 226, 113, 100, 200, 6, 241, 17, 43, 93, 39, 129, 19, 11, 250, 87, 37, 65, 10, 137, 180, 129, 87, 19, 60, 184, 123, 150, 18, 197, 28, 93, 65, 105, 14, 10, 121, 249, 196, 45, 132, 149, 88, 31, 104, 250, 77, 17, 253, 227, 131, 76, 93, 236, 147, 136, 251, 205, 106, 93, 22, 19, 40, 247, 107, 239, 13, 8, 8, 73, 27, 186, 224, 221, 61, 254, 224, 171, 83, 16, 5, 174, 207, 152, 153, 135, 224, 49, 63, 215, 115, 111, 239, 138, 13, 252, 42, 85] }) }
diff --git a/proptest-regressions/internet/ip_slice.txt b/proptest-regressions/internet/ip_slice.txt
new file mode 100644
index 0000000..288967c
--- /dev/null
+++ b/proptest-regressions/internet/ip_slice.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 f7d4ec238ea40baaf62ce1a2540534ed0ada9722ab1ad418c4b643ee161bd49a # shrinks to ipv4_header = Ipv4Header { dscp: Ipv4Dscp(0), ecn: Ipv4Ecn(0), total_len: 9656, identification: 0, dont_fragment: false, more_fragments: false, fragment_offset: IpFragOffset(0), time_to_live: 0, protocol: 227, header_checksum: 676, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [] }, ipv4_exts = Ipv4Extensions { auth: None }, ipv6_header = Ipv6Header { traffic_class: 101, flow_label: Ipv6FlowLabel(525200), payload_length: 7994, next_header: 112 (VRRP - Virtual Router Redundancy Protocol), hop_limit: 12, source: [137, 137, 220, 86, 155, 108, 52, 33, 107, 251, 21, 250, 167, 219, 173, 179], destination: [52, 171, 92, 104, 94, 162, 48, 5, 88, 249, 225, 56, 120, 82, 231, 181] }, mut ipv6_exts = Ipv6Extensions { hop_by_hop_options: Some(Ipv6RawExtHeader { next_header: 60 (IPv6-Opts - Destination Options for IPv6), payload: [191, 100, 141, 42, 44, 33, 62, 175, 128, 2, 94, 116, 32, 27, 246, 233, 241, 49, 138, 238, 90, 109, 63, 198, 125, 152, 156, 167, 89, 253, 97, 228, 141, 95, 15, 86, 56, 34, 66, 12, 182, 125, 183, 109, 210, 245, 36, 180, 129, 218, 149, 244, 171, 2, 180, 231, 57, 62, 87, 149, 71, 4, 151, 159, 149, 114, 188, 185, 243, 54, 129, 101, 71, 254, 38, 118, 27, 113, 86, 98, 208, 181, 185, 28, 27, 151, 73, 223, 25, 100, 144, 195, 119, 34, 209, 37, 61, 40, 27, 131, 157, 198, 114, 77, 207, 161, 143, 42, 144, 92, 156, 52, 60, 83, 36, 219, 122, 7, 44, 215, 95, 64, 48, 106, 42, 184, 30, 6, 247, 52, 42, 171, 39, 234, 162, 118, 29, 53, 95, 51, 218, 115, 222, 174, 12, 115, 92, 228, 143, 25, 109, 110, 142, 153, 0, 51, 101, 124, 90, 116, 76, 112, 29, 151, 225, 29, 227, 163, 71, 29, 211, 16, 59, 34, 204, 154, 73, 215, 251, 18, 77, 33, 236, 33, 37, 5, 130, 162, 135, 194, 177, 93, 189, 125, 61, 200, 13, 96, 94, 165, 16, 151, 245, 238, 154, 172, 127, 98, 230, 241, 224, 35, 104, 145, 252, 221, 14, 253, 122, 160, 221, 154, 29, 221, 233, 72, 176, 49, 177, 150, 97, 235, 94, 29, 136, 32, 86, 179, 4, 135, 141, 171, 98, 233, 247, 163, 108, 144, 196, 196, 59, 218, 194, 195, 105, 191, 26, 117, 112, 249, 168, 102, 189, 1, 9, 186, 234, 137, 190, 183, 221, 251, 92, 222, 200, 51, 5, 24, 125, 47, 113, 129, 214, 63, 10, 239, 82, 0, 63, 173, 207, 119, 79, 243, 197, 79, 132, 95, 121, 99, 218, 165, 28, 16, 48, 147, 254, 27, 29, 105, 169, 91, 25, 188, 143, 38, 20, 93, 10, 192, 127, 156, 16, 149, 127, 106, 150, 59, 234, 94, 202, 225, 187, 57, 68, 229, 242, 91, 159, 236, 152, 195, 240, 151, 74, 143, 18, 185, 163, 151, 238, 177, 225, 27, 184, 32, 247, 217, 4, 35, 116, 30, 15, 50, 53, 63, 122, 221, 249, 199, 221, 26, 133, 119, 221, 125, 87, 241, 70, 192, 78, 213, 72, 55, 28, 232, 249, 167, 189, 46, 174, 120, 85, 106, 39, 54, 252, 236, 16, 177, 205, 54, 224, 214, 76, 128, 250, 4, 59, 135, 210, 236, 173, 184, 51, 31, 88, 2, 82, 15, 21, 102, 232, 159, 119, 186, 92, 140, 122, 26, 75, 105, 199, 27, 124, 117, 48, 18, 222, 50, 180, 118, 109, 148, 201, 26, 255, 104, 111, 54, 12, 250, 182, 94, 69, 247, 191, 184, 131, 75, 205, 210, 191, 54, 211, 34, 154, 75, 237, 137, 22, 147, 176, 187, 254, 225, 83, 236, 39, 8, 88, 94, 250, 53, 236, 52, 132, 230, 167, 126, 235, 168, 64, 248, 8, 68, 71, 110, 67, 54, 4, 33, 184, 223, 185, 255, 12, 129, 141, 220, 65, 166, 113, 156, 202, 238, 247, 243, 116, 197, 205, 2, 6, 49, 69, 96, 251, 177, 200, 177, 68, 149, 205, 248, 15, 113, 161, 9, 239, 203, 36, 98, 2, 156, 167, 77, 228, 152, 55, 21, 126, 53, 4, 38, 13, 136, 77, 56, 11, 246, 136, 138, 198, 151, 96, 84, 157, 113, 236, 231, 92, 32, 155, 219, 43, 39, 225, 166, 51, 224, 46, 232, 168, 115, 229, 226, 248, 136, 84, 25, 196, 247, 231, 129, 104, 236, 17, 115, 95, 41, 219, 21, 186, 145, 104, 74, 85, 205, 161, 207, 38, 99, 215, 56, 159, 228, 240, 244, 134, 206, 71, 177, 85, 201, 250, 22, 185, 179, 64, 189, 102, 113, 184, 79, 255, 196, 162, 4, 2, 69, 225, 95, 134, 73, 220, 69, 7, 196, 56, 222, 247, 234, 24, 239, 96, 44, 250, 68, 63, 95, 170, 8, 215, 31, 166, 189, 208, 107, 119, 154, 46, 48, 75, 141, 213, 211, 11, 163, 28, 254, 37, 50, 247, 247, 113, 175, 17, 151, 6, 166, 198, 204, 242, 171, 14, 195, 137, 194, 70, 246, 11, 166, 117, 41, 113, 183, 111, 39, 227, 59, 227, 146, 149, 122, 138, 27, 200, 11, 121, 139, 180, 179, 213, 88, 119, 114, 13, 125, 122, 65, 61, 65, 67, 95, 82, 58, 86, 51, 168, 174, 128, 118, 100, 48, 14, 243, 99, 165, 23, 50, 34, 110, 29, 82, 59, 99, 196, 64, 158, 207, 232, 209, 27, 99, 123, 22, 79, 172, 11, 88, 45, 33, 75, 26, 24, 78, 115, 63, 202, 233, 0, 28, 249, 45, 239, 133, 0, 203, 229, 97, 69, 76, 130, 51, 175, 242, 25, 87, 183, 62, 128, 101, 129, 103, 69, 36, 78, 201, 101, 159, 133, 178, 175, 48, 205, 174, 89, 168, 67, 167, 147, 68, 229, 39, 201, 132, 205, 175, 186, 190, 199, 88, 40, 2, 247, 69, 66, 141, 230, 74, 219, 103, 214, 246, 77, 72, 185, 238, 218, 203, 250, 109, 212, 112, 226, 158, 36, 99, 1, 111, 41, 94, 192, 67, 125, 235, 11, 145, 109, 164, 234, 22, 86, 155, 181, 171, 58, 17, 107, 149, 178, 29, 245, 201, 97, 8, 83, 37, 15, 2, 149, 207, 176, 202, 47, 181, 121, 147, 223, 25, 113, 210, 234, 188, 209, 17, 217, 161, 163, 55, 220, 8, 28, 201, 168, 126, 235, 151, 13, 244, 151, 214, 15, 105, 202, 22, 76, 220, 189, 77, 188, 65, 196, 244, 63, 16, 138, 17, 2, 185, 13, 236] }), destination_options: Some(Ipv6RawExtHeader { next_header: 43 (IPv6-Route - Routing Header for IPv6), payload: [80, 82, 197, 37, 23, 231, 204, 115, 111, 115, 120, 70, 238, 153, 50, 141, 133, 173, 106, 24, 31, 138, 70, 73, 118, 110, 243, 164, 39, 41, 170, 203, 96, 118, 112, 202, 85, 227, 72, 109, 228, 112, 242, 105, 135, 77, 68, 27, 55, 96, 232, 128, 253, 22, 235, 179, 69, 106, 106, 137, 105, 25, 213, 215, 142, 21, 235, 101, 125, 15, 69, 12, 57, 48, 195, 115, 32, 69, 18, 251, 150, 191, 67, 171, 242, 44, 234, 231, 77, 209, 232, 33, 52, 58, 95, 255, 192, 177, 94, 96, 214, 234, 41, 195, 194, 216, 41, 223, 112, 242, 159, 102, 196, 3, 233, 209, 211, 4, 170, 126, 115, 245, 234, 196, 88, 158, 226, 186, 107, 90, 104, 189, 120, 70, 65, 124, 144, 186, 177, 105, 12, 201, 83, 87, 121, 34, 115, 141, 73, 231, 204, 178, 145, 54, 90, 180, 58, 81, 162, 93, 111, 2, 89, 215, 143, 180, 154, 191, 165, 207, 148, 15, 152, 212, 229, 124, 167, 120, 60, 253, 122, 232, 143, 48, 193, 4, 122, 27, 233, 112, 221, 135, 190, 218, 219, 228, 15, 145, 86, 211, 193, 86, 237, 229, 121, 89, 100, 180, 138, 74, 32, 234, 2, 142, 233, 228, 148, 99, 100, 84, 226, 127, 127, 124, 108, 226, 148, 96, 52, 29, 70, 9, 113, 81, 113, 196, 102, 226, 72, 249, 152, 71, 62, 132, 247, 92, 29, 160, 121, 134, 175, 151, 78, 88, 54, 116, 59, 234, 234, 161, 82, 219, 185, 233, 219, 1, 244, 0, 184, 225, 230, 235, 122, 134, 111, 184, 118, 83, 180, 243, 113, 124, 226, 162, 50, 162, 182, 177, 210, 127, 24, 71, 80, 57, 190, 78, 207, 40, 63, 78, 87, 66, 217, 66, 97, 136, 43, 207, 216, 7, 246, 226, 81, 100, 249, 195, 26, 143, 237, 9, 245, 121, 91, 158, 113, 135, 242, 51, 110, 249, 196, 74, 41, 177, 204, 10, 165, 79, 215, 69, 109, 229, 91, 166, 41, 178, 162, 57, 142, 125, 162, 216, 133, 113, 182, 122, 140, 11, 26, 51, 182, 124, 200, 127, 61, 253, 220, 139, 7, 95, 101, 88, 20, 64, 94, 201, 141, 159, 229, 58, 162, 53, 85, 36, 92, 137, 254, 30, 230, 250, 249, 130, 253, 248, 44, 227, 31, 239, 63, 95, 4, 21, 19, 107, 215, 117, 96, 130, 252, 25, 37, 171, 207, 48, 173, 15, 118, 16, 239, 169, 225, 149, 185, 253, 39, 166, 11, 216, 250, 20, 130, 120, 230, 148, 67, 202, 73, 83] }), routing: Some(Ipv6RoutingExtensions { routing: Ipv6RawExtHeader { next_header: 44 (IPv6-Frag - Fragment Header for IPv6), payload: [85, 24, 36, 128, 200, 198, 164, 125, 213, 0, 161, 132, 146, 215, 91, 54, 119, 215, 0, 87, 118, 254, 39, 245, 172, 214, 184, 105, 19, 57, 223, 228, 1, 180, 211, 105, 124, 221, 140, 252, 44, 38, 166, 108, 82, 29, 171, 163, 139, 248, 36, 191, 50, 44, 44, 15, 147, 237, 224, 253, 69, 0, 50, 224, 202, 57, 94, 29, 133, 85, 217, 213, 248, 148, 189, 163, 62, 94, 207, 132, 233, 155, 128, 46, 135, 104, 92, 153, 16, 118, 45, 142, 185, 137, 71, 108, 89, 16, 157, 133, 57, 25, 255, 212, 154, 74, 243, 75, 207, 227, 150, 86, 210, 6, 185, 73, 115, 48, 6, 20, 229, 250, 213, 145, 213, 34, 103, 71, 31, 159, 119, 2, 153, 47, 88, 166, 116, 141, 45, 229, 35, 6, 238, 159, 15, 172, 46, 238, 113, 152, 183, 114, 45, 82, 89, 49, 42, 82, 172, 104, 23, 104, 163, 175, 114, 30, 82, 224, 72, 33, 195, 208, 232, 157, 97, 127, 201, 104, 56, 23, 250, 227, 61, 186, 132, 236, 106, 72, 185, 188, 198, 75, 116, 189, 135, 148, 152, 196, 140, 126, 189, 52, 0, 84, 25, 227, 168, 128, 134, 86, 126, 98, 71, 201, 8, 30, 108, 130, 220, 190, 106, 150, 63, 95, 162, 25, 142, 183, 166, 83, 240, 81, 173, 88, 152, 180, 74, 104, 50, 50, 197, 48, 52, 190, 166, 128, 109, 187, 71, 84, 232, 246, 108, 214, 196, 111, 251, 51, 154, 131, 236, 146, 115, 120, 48, 191, 201, 57, 81, 206, 250, 205, 105, 254, 90, 232, 65, 157, 154, 62, 98, 54, 153, 54, 57, 224, 175, 124, 208, 252, 104, 180, 177, 175, 22, 95, 72, 99, 104, 209, 11, 17, 200, 255, 26, 146, 249, 175, 151, 99, 34, 24, 197, 193, 228, 242, 48, 198, 122, 220, 5, 20, 110, 0, 113, 227, 45, 29, 17, 112, 252, 170, 104, 250, 114, 84, 164, 94, 25, 202, 58, 65, 205, 231, 238, 56, 239, 159, 34, 231, 220, 61, 254, 214, 160, 42, 104, 222, 93, 137, 18, 9, 82, 27, 238, 2, 187, 242, 212, 193, 203, 19, 100, 82, 66, 110, 149, 157, 59, 77, 233, 164, 142, 247, 224, 117, 230, 113, 142, 59, 19, 235, 150, 125, 136, 28, 150, 55, 70, 194, 131, 209, 144, 194, 32, 249, 135, 177, 205, 50, 133, 250, 194, 129, 226, 57, 76, 231, 12, 102, 179, 20, 233, 46, 66, 108, 108, 86, 174, 106, 175, 250, 163, 231, 184, 208, 62, 127, 73, 231, 48, 59, 2, 72, 216, 199, 197, 112, 117, 151, 84, 83, 118, 79, 244, 34, 238, 44, 164, 162, 34, 5, 113, 187, 88, 18, 0, 76, 44, 176, 16, 31, 179, 214, 188, 113, 253, 218, 210, 126, 165, 246, 37, 93, 59, 126, 66, 6, 138, 53, 152, 235, 221, 30, 194, 49, 58, 36, 254, 178, 184, 255, 171, 86, 25, 212, 49, 255, 217, 170, 102, 29, 79, 160, 252, 2, 128, 196, 102, 248, 74, 4, 184, 254, 68, 20, 106, 7, 194, 202, 89, 146, 95, 174, 71, 233, 149, 68, 224, 240, 72, 230, 188, 155, 228, 203, 235, 107, 157, 25, 24, 152, 213, 67, 13, 62, 100, 37, 168, 19, 229, 89, 83, 22, 87, 33, 156, 78, 115, 108, 81, 86, 248, 251, 113, 225, 124, 86, 47, 23, 79, 32, 244, 233, 221, 4, 177, 144, 94, 57, 77, 21, 0, 76, 134, 126, 238, 173, 5, 144, 67, 52, 171, 164, 129, 191, 220, 209, 249, 14, 67, 81, 136, 27, 161, 237, 4, 145, 95, 35, 110, 205, 37, 125, 87, 222, 135, 73, 230, 212, 197, 75, 125, 196, 121, 117, 36, 117, 40, 159, 158, 71, 140, 189, 63, 212] }, final_destination_options: Some(Ipv6RawExtHeader { next_header: 17 (UDP - User Datagram), payload: [11, 156, 183, 140, 42, 132, 111, 189, 181, 97, 104, 33, 150, 92, 113, 139, 87, 124, 53, 34, 15, 252, 175, 60, 96, 40, 58, 249, 60, 255, 235, 57, 155, 35, 103, 109, 35, 132, 26, 84, 56, 17, 104, 128, 77, 24, 187, 5, 100, 16, 101, 66, 214, 185, 84, 172, 64, 93, 220, 154, 34, 178, 98, 174, 159, 109, 161, 244, 250, 190, 138, 115, 24, 217, 124, 247, 210, 136, 103, 195, 148, 50, 134, 254, 90, 63, 23, 189, 145, 162, 222, 82, 105, 148, 156, 251, 133, 247, 91, 53, 203, 135, 196, 185, 1, 223, 227, 152, 191, 215, 57, 197, 72, 114, 171, 249, 210, 197, 118, 35, 190, 37, 175, 92, 187, 37, 33, 61, 39, 186, 51, 185, 217, 218, 38, 79, 200, 9, 167, 115, 247, 3, 207, 221, 84, 208, 162, 3, 88, 150, 139, 42, 112, 86, 124, 43, 117, 93, 69, 62, 45, 72, 62, 189, 194, 208, 52, 189, 186, 116, 104, 157, 207, 104, 94, 185, 82, 216, 151, 53, 100, 57, 194, 245, 99, 115, 171, 126, 4, 106, 161, 247, 175, 253, 111, 217, 193, 2, 31, 119, 29, 134, 240, 154, 54, 163, 250, 86, 76, 57, 251, 135, 163, 225, 158, 65, 203, 109, 195, 63, 106, 42, 125, 48, 23, 115, 114, 81, 63, 117, 129, 210, 8, 175, 87, 169, 243, 115, 52, 124, 31, 210, 184, 59, 178, 30, 111, 97, 141, 128, 69, 148, 234, 44, 242, 174, 25, 243, 33, 216, 38, 4, 39, 158, 24, 173, 186, 42, 201, 141, 32, 242, 42, 181, 17, 243, 162, 52, 144, 211, 169, 64, 9, 75, 238, 42, 125, 190, 192, 61, 165, 85, 158, 93, 82, 40, 197, 11, 165, 212, 96, 183, 224, 203, 10, 3, 26, 247, 131, 35, 54, 62, 216, 17, 40, 125, 235, 207, 141, 41, 26, 163, 188, 88, 68, 79, 47, 40, 100, 205, 109, 56, 234, 2, 104, 46, 107, 7, 247, 166, 68, 129, 211, 53, 183, 203, 224, 170, 242, 190, 235, 55, 122, 221, 137, 78, 189, 112, 103, 116, 61, 177, 1, 254, 206, 223, 243, 152, 192, 237, 47, 163, 203, 50, 78, 87, 208, 143, 221, 148, 144, 42, 19, 193, 1, 28, 61, 151, 234, 60, 227, 219, 207, 242, 89, 66, 79, 34, 57, 106, 207, 202, 224, 193, 62, 50, 216, 178, 93, 66, 37, 91, 215, 7, 16, 76, 48, 203, 63, 247, 47, 183, 102, 157, 158, 23, 52, 176, 56, 141, 100, 246, 21, 239, 202, 130, 24, 145, 246, 175, 176, 203, 214, 234, 60, 173, 138, 248, 105, 16, 232, 216, 23, 120, 1, 83, 99, 136, 89, 109, 92, 228, 212, 146, 207, 174, 138, 172, 240, 250, 68, 79, 105, 112, 48, 240, 172, 250, 7, 207, 141, 161, 54, 179, 229, 218, 173, 12, 42, 195, 85, 154, 89, 89, 39, 189, 31, 160, 34, 72, 189, 252, 198, 14, 49, 249, 131, 184, 23, 184, 56, 219, 117, 39, 206, 171, 106, 66, 248, 69, 115, 60, 113, 228, 114, 35, 17, 175, 78, 172, 30, 64, 196, 83, 140, 3, 253, 128, 5, 112, 120, 180, 0, 225, 87, 223, 150, 6, 227, 72, 67, 46, 79, 123, 2, 210, 131, 66, 218, 137, 210, 138, 55, 178, 26, 184, 227, 155, 23, 38, 208, 195, 42, 200, 79, 184, 24, 47, 151, 253, 120, 20, 208, 104, 10, 171, 88, 121, 65, 203, 24, 181, 94, 194, 231, 93, 72, 114, 12, 38, 243, 242, 50, 189, 62, 160, 144, 231, 222, 15, 169, 177, 239, 232, 172, 242, 255, 32, 190, 85, 29, 95, 222, 236, 79, 252, 25, 92, 216, 102, 41, 69, 139, 53, 156, 40, 119, 66, 114, 90, 117, 149, 86, 203, 204, 187, 225, 211, 101, 93, 185, 207, 252, 61, 43, 237, 17, 134, 40, 225, 177, 1, 212, 137, 120, 146, 128, 238, 225, 67, 25, 214, 203, 183, 174, 236, 47, 58, 214, 66, 35, 86, 106, 192, 10, 254, 216, 239, 145, 229, 147, 91, 203, 145, 202, 103, 33, 123, 206, 120, 101, 116, 51, 103, 205, 1, 92, 69, 91, 209, 205, 235, 139, 64, 234, 253, 97, 131, 69, 21, 219, 144, 46, 22, 138, 8, 251, 70, 116, 225, 117, 42, 207, 238, 128, 124, 34, 28, 62, 29, 25, 124, 91, 68, 125, 38, 162, 223, 171, 31, 200, 124, 234, 121, 176, 187, 78, 111, 65, 11, 67, 203, 220, 22, 56, 76, 109, 218, 187, 234, 185, 88, 249, 219, 110, 135, 151, 194, 86, 234, 28, 51, 113, 150, 114, 241, 51, 250, 163, 182, 190, 27, 34, 41, 229, 169, 1, 154, 241, 171, 213, 66, 151, 121, 133, 12, 230, 98, 237, 160, 89, 125, 169, 40, 199, 110, 229, 142, 4, 200, 26, 99, 37, 125, 81, 170, 67, 146, 245, 86, 248, 211, 228, 193, 211, 149, 50, 15, 194, 77, 186, 63, 69, 97, 71, 84, 155, 56, 214, 84, 207, 128, 145, 208, 110, 187, 55, 57, 217, 24, 180, 143, 151, 56, 214, 71, 60, 154, 224, 150, 135, 175, 29, 162, 239, 88, 111, 202, 97, 156, 135, 13, 192, 60, 149, 20, 91, 207, 40, 5, 222, 199, 168, 0, 78, 241, 138, 12, 19, 35, 221, 82, 118, 18, 237, 71, 166, 7, 149, 25, 17, 250, 51, 201, 238, 229, 225, 62, 73, 40, 245, 255, 119, 137, 89, 88, 243, 143, 172, 77, 138, 255, 245, 154, 164, 203, 80, 229, 208, 179, 236, 229, 80, 191, 65, 196, 29, 97, 31, 83, 147, 77, 167, 6, 179, 180, 179, 46, 236, 148, 24, 118, 115, 253, 93, 187, 185, 200, 8, 104, 225, 0, 121, 183, 91, 169, 28, 106, 119, 253, 178, 158, 66, 72, 164, 97, 63, 125, 232, 133, 207, 233, 132, 51, 81, 38, 206, 238, 92, 132, 20, 72, 60, 196, 125, 41, 213, 76, 123, 224, 133, 77, 223, 15, 143, 141, 158, 229, 45, 212, 224, 10, 160, 190, 20, 26, 66, 194, 186, 130, 176, 29, 247, 12, 196, 85, 69, 232, 223, 32, 126, 46, 4, 184, 222, 97, 28, 236, 104, 16, 13, 189, 149, 19, 34, 123, 90, 249, 76, 130, 41, 245, 33, 166, 193, 212, 187, 48, 121, 125, 196, 40, 150, 106, 16, 206, 86, 186, 184, 0, 196, 175, 76, 74, 17, 187, 1, 16, 191, 58, 5, 168, 193, 153, 234, 169, 106, 97, 207, 34, 176, 201, 80, 122, 195, 173, 112, 149, 118, 75, 90, 48, 213, 7, 19, 172, 183, 83, 251, 39, 53, 35, 24, 101, 228, 217, 70, 51, 168, 37, 246, 11, 47, 68, 176, 19, 237, 233, 161, 113, 79, 210, 22, 33, 78, 92, 128, 203, 20, 115, 39, 93, 141, 117, 217, 217, 166, 175, 135, 125, 167, 114, 180, 62, 15, 211, 238, 200, 56, 117, 28, 9, 113, 102, 165, 19, 251, 76, 100, 153, 239, 246, 188, 50, 112, 34, 99, 83, 96, 93, 63, 174, 50, 111, 142, 90, 89, 60, 153, 185, 223, 20, 87, 18, 174, 167, 234, 150, 55, 8, 144, 254, 140, 244, 139, 95, 96, 95, 33, 228, 69, 5, 182, 168, 151, 93, 115, 237, 2, 47, 149, 59, 250, 138, 157, 105, 96, 21, 186, 174, 75, 36, 245, 169, 9, 6, 130, 117, 93, 253, 216, 105, 205, 40, 11, 123, 149, 75, 78, 127, 150, 240, 36, 196, 153, 47, 181, 87, 61, 118, 226, 234, 136, 173, 6, 125, 126, 108, 235, 119, 38, 115, 233, 180, 24, 233, 84, 243, 161, 90, 12, 116, 23, 214, 237, 86, 126, 193, 123, 10, 206, 54, 214, 97, 13, 244, 232, 11, 115, 129, 206, 163, 151, 43, 214, 150, 55, 21, 69, 140, 165, 22, 144, 193, 148, 94, 98, 16, 43, 1, 58, 138, 162, 210, 227, 107, 148, 141, 25, 141, 204, 10, 225, 121, 130, 61, 143, 66, 6, 68, 110, 178, 98, 124, 125, 33, 15, 98, 199, 136, 163, 19, 81, 40, 252, 77, 51, 76, 254, 97, 240, 139, 247, 230, 171, 79, 163, 21, 192, 156, 80, 166, 51, 85, 8, 189, 241, 124, 128, 82, 224, 219, 221, 225, 111, 185, 225, 236, 22, 38, 29, 149, 225, 0, 100, 125, 148, 108, 105, 178, 97, 86, 143, 118] }) }), fragment: Some(Ipv6FragmentHeader { next_header: 60 (IPv6-Opts - Destination Options for IPv6), fragment_offset: IpFragOffset(5532), more_fragments: false, identification: 338582946 }), auth: None }
+cc ce576bdbf3c552b5e9248275cffe5931a95a63bca29cf1c610d22a494fb6a17e # shrinks to ipv4_header = Ipv4Header { dscp: Ipv4Dscp(51), ecn: Ipv4Ecn(2), total_len: 37179, identification: 34694, dont_fragment: false, more_fragments: false, fragment_offset: IpFragOffset(181), time_to_live: 83, protocol: 207, header_checksum: 35262, source: [0, 0, 0, 0], destination: [0, 0, 0, 29], options: [] }, ipv4_exts = Ipv4Extensions { auth: Some(IpAuthHeader { next_header: 17 (UDP - User Datagram), spi: 888998640, sequence_number: 2864600963, raw_icv: [160, 42, 65, 169, 182, 49, 97, 188, 29, 207, 68, 63, 177, 208, 110, 218, 203, 58, 119, 12, 170, 69, 156, 101, 132, 160, 153, 198, 35, 229, 254, 228, 136, 67, 201, 236, 243, 233, 97, 91, 26, 2, 171, 202, 116, 163, 231, 6, 148, 175, 208, 60, 184, 89, 70, 189, 186, 77, 250, 154, 153, 163, 245, 232, 128, 178, 154, 25, 193, 81, 198, 21, 195, 13, 191, 212, 201, 214, 223, 109, 9, 133, 184, 221, 149, 37, 104, 73, 238, 177, 133, 38, 228, 156, 168, 111, 219, 112, 66, 6, 66, 13, 201, 59, 142, 129, 111, 223, 208, 87, 129, 80, 178, 251, 81, 187, 50, 188, 224, 250, 10, 200, 90, 90, 72, 199, 179, 242, 90, 72, 189, 114, 25, 84, 235, 135, 233, 40, 34, 111, 127, 101, 252, 29, 26, 78, 155, 53, 197, 54, 203, 110, 10, 58, 42, 139, 44, 44, 181, 80, 245, 165, 149, 165, 128, 229, 70, 242, 49, 146, 42, 197, 40, 39, 140, 117, 17, 91, 205, 207, 156, 37, 249, 29, 64, 236, 93, 196, 126, 165, 251, 225, 99, 46, 55, 246, 51, 223, 233, 75, 216, 33, 8, 4, 1, 18, 196, 88, 53, 18, 165, 149, 116, 201, 33, 37, 193, 91, 64, 195, 197, 20, 153, 221, 156, 22, 56, 42, 216, 194, 46, 99, 240, 41, 230, 107, 155, 54, 150, 181, 80, 205, 182, 253, 210, 245, 20, 108, 192, 59, 73, 41, 225, 36, 213, 161, 115, 87, 171, 3, 126, 19, 228, 8, 237, 210, 104, 156, 202, 177, 186, 41, 204, 68, 54, 231, 49, 42, 155, 107, 3, 37, 119, 11, 207, 139, 40, 41, 110, 145, 218, 68, 244, 122, 245, 138, 91, 77, 228, 2, 15, 3, 74, 39, 213, 129, 145, 234, 173, 71, 46, 67, 63, 58, 50, 190, 123, 49, 38, 123, 162, 137, 81, 122, 183, 221, 172, 195, 130, 130, 252, 218, 22, 241, 174, 146, 156, 162, 76, 238, 77, 15, 136, 180, 189, 236, 143, 208, 104, 204, 97, 255, 204, 38, 229, 18, 45, 253, 41, 80, 17, 222, 185, 250, 35, 49, 24, 34, 159, 222, 136, 90, 225, 107, 74, 121, 63, 249, 199, 158, 79, 110, 137, 104, 3, 253, 5, 51, 18, 104, 186, 78, 2, 151, 65, 248, 117, 142, 222, 38, 102, 3, 37, 110, 129, 44, 227, 193, 0, 84, 245, 72, 197, 49, 83, 116, 224, 191, 167, 14, 220, 51, 179, 126, 145, 1, 147, 41, 119, 107, 61, 234, 87, 146, 111, 14, 240, 181, 120, 103, 100, 24, 107, 167, 182, 152, 221, 8, 207, 129, 123, 47, 164, 14, 241, 220, 203, 175, 236, 127, 16, 243, 237, 49, 15, 129, 10, 57, 221, 6, 172, 13, 103, 111, 229, 149, 145, 38, 72, 237, 159, 35, 223, 249, 217, 89, 115, 66, 103, 247, 30, 79, 99, 171, 166, 185, 215, 38, 222, 237, 239, 234, 222, 107, 124, 146, 65, 206, 145, 225, 71, 118, 179, 232, 89, 214, 42, 188, 221, 144, 206, 24, 100, 31, 253, 230, 33, 129, 140, 92, 155, 199, 250, 194, 26, 88, 230, 240, 133, 138, 178, 1, 226, 123, 49, 185, 137, 33, 90, 57, 53, 40, 20, 187, 240, 8, 69, 221, 151, 223, 195, 91, 243, 168, 193, 102, 43, 225, 146, 76, 218, 129, 166, 249, 85, 109, 169, 136, 168, 130, 137, 243, 54, 32, 91, 250, 25, 159, 109, 25, 31, 243, 111, 44, 104, 81, 224, 101, 53, 12, 41, 30, 71, 186, 220, 107, 77, 124, 153, 96, 107, 188, 195, 52, 13, 51, 99, 199, 171, 99, 62, 152, 174, 130, 148, 229, 3, 203, 241, 165, 111, 127, 131, 156, 84, 30, 117, 128, 171, 159, 77, 51, 117, 94, 61, 36, 250, 109, 183, 56, 191, 13, 80, 165, 140, 200, 136, 44, 116, 238, 248, 140, 34, 32, 190, 190, 65, 38, 204, 137, 192, 217, 206, 42, 221, 147, 33, 76, 208, 173, 250, 148, 222, 3, 235, 61, 10, 196, 25, 145, 3, 87, 240, 189, 131, 5, 116, 198, 2, 32, 112, 49, 169, 111, 46, 169, 232, 240, 251, 61, 207, 35, 155, 45, 106, 47, 226, 170, 133, 75, 37, 162, 81, 92, 178, 222, 202, 82, 21, 26, 26, 95, 183, 249, 168, 52, 201, 54, 61, 118, 12, 46, 80, 62, 170, 218, 121, 187, 120, 55, 206, 11, 67, 2, 93, 133, 10, 72, 51, 93, 18, 247, 199, 165, 134, 113, 251, 84, 44, 195, 160, 221, 94, 220, 12, 133, 30, 176, 214, 97, 66, 158, 77, 73, 226, 55, 27, 0, 190, 122, 205, 19, 247, 125, 203, 47, 20, 202, 189, 161, 113, 123, 253, 227, 150, 199, 180, 226, 14, 5, 188, 222, 130, 230, 94, 187, 244, 20, 58, 33, 247, 47, 204, 74, 52, 243, 73, 22, 105, 178, 229, 95, 0, 230, 144, 41, 13, 241, 71, 131, 179, 179, 146, 78, 7, 242, 23, 242, 152, 251, 46, 9, 96, 247, 118, 41, 23, 38, 156, 221, 132, 19, 232, 173, 190, 250, 205, 239, 48, 48, 235, 70, 163, 158, 102, 53, 22, 226, 60, 85, 103, 124, 87, 91, 205, 44, 114, 255, 167, 155, 111, 18, 43, 136, 109, 203, 182, 160, 164, 224, 174, 255, 97, 220, 185, 81, 173, 183] }) }, ipv6_header = Ipv6Header { traffic_class: 89, flow_label: Ipv6FlowLabel(266446), payload_length: 31614, next_header: 18 (MUX - Multiplexing), hop_limit: 141, source: [242, 97, 86, 55, 43, 220, 197, 113, 212, 190, 208, 174, 8, 16, 107, 88], destination: [238, 176, 72, 31, 225, 156, 176, 69, 192, 108, 135, 115, 177, 43, 211, 94] }, mut ipv6_exts = Ipv6Extensions { hop_by_hop_options: None, destination_options: Some(Ipv6RawExtHeader { next_header: 43 (IPv6-Route - Routing Header for IPv6), payload: [253, 216, 90, 154, 46, 215, 92, 35, 0, 21, 68, 154, 201, 72, 251, 131, 180, 68, 106, 163, 45, 89, 59, 170, 63, 154, 235, 149, 176, 240, 100, 171, 130, 221, 30, 196, 227, 142, 151, 152, 240, 15, 192, 144, 153, 40, 146, 214, 78, 89, 237, 213, 198, 34, 249, 2, 198, 127, 122, 196, 134, 198, 29, 85, 66, 41, 200, 210, 245, 115, 169, 180, 240, 204, 161, 156, 235, 192, 106, 209, 111, 109, 49, 117, 253, 204, 66, 70, 253, 194, 107, 85, 152, 110, 242, 86, 254, 207, 206, 135, 178, 117, 82, 94, 88, 64, 209, 68, 189, 63, 11, 94, 175, 19, 211, 105, 99, 90, 93, 15, 146, 248, 41, 27, 161, 130, 64, 201, 146, 55, 185, 255, 238, 136, 130, 152, 236, 240, 30, 38, 21, 182, 150, 244, 126, 169, 42, 224, 174, 145, 121, 253, 2, 136, 238, 126, 186, 113, 179, 55, 240, 153, 52, 102, 180, 179, 52, 23, 221, 36, 8, 171, 228, 137, 153, 232, 144, 240, 238, 11, 109, 58, 151, 84, 158, 233, 107, 202, 244, 33, 113, 137, 94, 232, 43, 211, 199, 104, 10, 193, 61, 37, 47, 112, 198, 80, 105, 14, 99, 244, 211, 136, 130, 52, 2, 152, 159, 217, 135, 74, 249, 14, 102, 33, 149, 83, 219, 208, 247, 27, 255, 171, 132, 243, 27, 78, 34, 193, 60, 42, 52, 206, 26, 24, 186, 117, 171, 2, 209, 119, 185, 26, 229, 58, 243, 130, 84, 98, 223, 65, 132, 1, 46, 180, 248, 148, 72, 169, 155, 143, 53, 145, 182, 169, 249, 83, 239, 25, 135, 161, 47, 216, 150, 171, 17, 53, 146, 133, 102, 150, 127, 117, 246, 84, 151, 23, 62, 210, 21, 175, 232, 180, 16, 69, 66, 238, 48, 121, 1, 186, 170, 63, 149, 13, 122, 213, 157, 236, 36, 224, 175, 136, 15, 123, 247, 135, 188, 111, 53, 119, 122, 149, 88, 119, 28, 41, 18, 140, 234, 103, 33, 19, 95, 179, 148, 61, 233, 96, 75, 57, 77, 116, 240, 19, 76, 54, 175, 238, 56, 121, 93, 2, 35, 162, 153, 110, 66, 97, 232, 44, 192, 197, 26, 76, 204, 183, 247, 205, 85, 15, 109, 250, 5, 249, 10, 16, 1, 95, 157, 102, 196, 50, 167, 0, 76, 166, 215, 147, 204, 233, 3, 236, 82, 174, 33, 125, 144, 44, 133, 82, 37, 210, 93, 203, 127, 197, 115, 245, 190, 168, 10, 203, 128, 50, 167, 105, 115, 196, 63, 33, 75, 200, 49, 161, 244, 30, 236, 22, 82, 76, 12, 229, 138, 139, 32, 177, 83, 79, 119, 114, 85, 235, 253, 253, 228, 184, 157, 189, 19, 172, 105, 58, 14, 231, 1, 96, 70, 149, 48, 13, 132, 245, 63, 78, 113, 180, 203, 132, 182, 65, 171, 223, 39, 14, 169, 57, 193, 126, 207, 125, 4, 73, 40, 41, 168, 30, 25, 199, 187, 98, 156, 162, 247, 212, 204, 72, 130, 156, 104, 118, 248, 104, 83, 30, 254, 210, 16, 123, 167, 146, 42, 44, 205, 42, 132, 135, 133, 223, 254, 71, 46, 68, 30, 122, 32, 147, 135, 169, 191, 148, 119, 19, 98, 16, 76, 133, 83, 98, 118, 19, 179, 223, 80, 67, 78, 244, 23, 133, 135, 189, 94, 198, 52, 50, 13, 88, 76, 48, 82, 153, 205, 83, 209, 47, 192, 229, 82, 33, 33, 41, 148, 103, 100, 71, 228, 191, 204, 77, 66, 45, 193, 149, 42, 166, 62, 6, 253, 249, 6, 67, 132, 200, 255, 65, 45, 31, 198, 100, 13, 156, 255, 135, 210, 82, 252, 141, 84, 206, 214, 69, 181, 133, 116, 26, 228, 17, 201, 74, 84, 130, 113, 208, 187, 183, 254, 55, 61, 63, 87, 148, 113, 231, 84, 146, 219, 131, 190, 56, 103, 3, 207, 27, 133, 83, 175, 95, 107, 89, 140, 26, 88, 242, 162, 72, 186, 124, 101, 214, 102, 152, 39, 11, 157, 59, 14, 228, 115, 89, 21, 180, 122, 241, 250, 26, 123, 221, 124, 234, 250, 74, 78, 197, 51, 15, 8, 144, 175, 122, 175, 128, 60, 166, 245, 182, 10, 4, 249, 29, 250, 119, 24, 208, 107, 19, 215, 102, 249, 178, 208, 191, 184, 157, 237, 20, 15, 134, 12, 94, 184, 162, 26, 104, 245, 68, 199, 171, 239, 25, 67, 48, 126, 104, 245, 252, 51, 191, 185, 59, 31, 109, 110, 223, 106, 192, 192, 236, 18, 250, 243, 51, 13, 182, 199, 203, 208, 38, 182, 125, 245, 34, 168, 192, 37, 210, 153, 78, 110, 136, 201, 49, 234, 104, 254, 67, 165, 129, 16, 190, 127, 209, 91, 203, 72, 38, 181, 45, 162, 120, 21, 117, 246, 171, 211, 137, 245, 168, 197, 86, 14, 153, 10, 123, 176, 146, 75, 253, 152, 206, 250, 41, 97, 164, 74, 65, 168, 45, 117, 114, 4, 138, 118, 50, 195, 71, 212, 170, 18, 237, 13, 238, 159, 97, 29, 188, 7, 44, 252, 123, 177, 17, 151, 94, 106, 123, 139, 61, 236, 34, 27, 244, 61, 156, 88, 251, 176, 30, 62, 30, 214, 159, 55, 156, 8, 72, 224, 47, 82, 50, 17, 7, 254, 134, 99, 184, 176, 83, 172, 167, 235, 45, 162, 212, 240, 221, 8, 219, 230, 213, 63, 79, 50, 6, 46, 65, 231, 21, 200, 114, 233, 90, 40, 242, 136, 47, 173, 76, 158, 134, 129, 12, 52, 100, 148, 226, 69, 38, 227, 138, 253, 242, 231, 10, 27, 13, 31, 244, 96, 172, 241, 44, 14, 7, 144, 125, 252, 69, 166, 34, 158, 138, 87, 145, 87, 80, 28, 9, 14, 211, 52, 129, 29, 8, 160, 86, 74, 90, 70, 134, 170, 177, 178, 99, 63, 215, 159, 105, 74, 80, 128, 52, 7, 199, 87, 223, 113, 216, 118, 110, 186, 107, 61, 106, 171, 0, 122, 177, 182, 23, 32, 23, 105, 229, 201, 147, 81, 216, 33, 45, 183, 210, 224, 209, 224, 61, 71, 254, 2, 157, 47, 72, 170, 156, 7, 14, 215, 238, 54, 239, 254, 228, 38, 66, 185, 229, 38, 177, 90, 33, 50, 112, 96, 203, 29, 213, 181, 121, 123, 214, 214, 169, 65, 55, 65, 159, 198, 131, 79, 186, 108, 21, 212, 66, 113, 196, 217, 163, 45, 132, 130, 75, 14, 72, 103, 101, 24, 248, 99, 153, 124, 9, 243, 206, 135, 43, 132, 22, 46, 178, 213, 63, 145, 82, 90, 156, 123, 81, 82, 50, 60, 138, 127, 207, 85, 74, 252, 164, 187, 192, 154, 28, 228, 21, 240, 157, 20, 85, 209, 192, 113, 86, 139, 180, 11, 52, 4, 11, 64, 207, 121, 189, 105, 91, 25, 51, 81, 44, 86, 137, 183, 118, 187, 133, 17, 55, 255, 38, 73, 150, 8, 49, 225, 252, 128, 6, 80, 229, 103, 174, 47, 68, 137, 64, 21, 162, 22, 186, 111, 20, 97, 84, 148, 86, 240, 253, 10, 230, 97, 69, 94, 136, 73, 88, 135, 105, 187, 190, 159, 191, 245, 220, 115, 210, 223, 200, 35, 153, 155, 142, 207, 66, 124, 133, 144, 48, 136, 103, 197, 61, 156, 84, 194, 25, 235, 92, 97, 96, 91, 58, 159, 105, 34, 218, 64, 166, 252, 232, 254, 92, 253, 21, 8, 140, 166, 231, 17, 3, 43, 169, 6, 210, 83, 43, 45, 216, 78, 194, 35, 55, 141, 102, 156, 48, 90, 109, 32, 235, 84, 252, 245, 133, 86, 142, 156, 252, 89, 88, 35, 243, 240, 68, 43, 30, 146, 243, 114, 150, 69, 147, 188, 213, 38, 177, 52, 74, 52, 228, 114, 229, 141, 6, 104, 195, 110, 197, 253, 198, 223, 234, 11, 158, 42, 88, 242, 62, 206, 24, 120, 245, 1, 59, 80, 164, 97, 101, 69, 171, 68, 18, 22, 233, 47, 251, 131, 86, 124, 229, 222, 132, 235, 27, 230, 229, 214, 68, 2, 176, 58, 224, 245, 32, 76, 177, 166, 2, 69, 5, 189, 94, 95, 153, 137, 91, 193, 46, 186, 241, 13, 163, 23, 230, 11, 173, 250, 76, 76, 154, 190, 18, 173, 245, 233, 154, 48, 58, 230, 37, 134, 227, 28, 175, 247, 28, 164, 230, 134, 201, 123, 43, 130, 17, 59, 142, 22, 77, 223, 31, 80, 14, 33, 115, 70, 212, 144, 67, 233, 75, 247, 121, 167, 215, 146, 164, 237, 233, 134, 111, 159, 12, 21, 113, 24, 87, 94, 237, 50, 243, 151, 37, 54, 23, 223, 124, 37, 8, 209, 224, 186, 152, 61, 139, 122, 134, 180, 34, 197, 217, 54, 250, 136, 114, 18, 151, 64, 154, 68, 44, 196, 80, 71, 4, 62, 96, 228, 90, 42, 150, 206, 120, 179, 253, 173, 238, 95, 94, 130, 46, 47, 137, 181, 29, 140, 254, 3, 231, 109, 215, 200, 32, 226, 130, 87, 35, 210, 225, 35, 205, 201, 253, 35, 66, 24, 155, 110, 96, 206, 136, 175, 49, 50, 207, 76, 119, 132, 12, 170, 224, 214, 177, 250, 140, 81, 53, 132, 156, 42, 194, 238, 34, 69, 121, 196, 126, 143, 123, 104, 100, 145, 246, 182, 163, 196, 52] }), routing: Some(Ipv6RoutingExtensions { routing: Ipv6RawExtHeader { next_header: 44 (IPv6-Frag - Fragment Header for IPv6), payload: [25, 34, 82, 77, 97, 70, 43, 155, 198, 155, 224, 73, 40, 13, 161, 254, 240, 134, 156, 99, 192, 201, 39, 202, 228, 246, 95, 119, 98, 229, 90, 37, 128, 60, 198, 109, 12, 111, 133, 8, 154, 97, 78, 96, 116, 201, 132, 21, 204, 105, 108, 111, 117, 47, 93, 217, 119, 74, 193, 119, 15, 249, 184, 115, 246, 69, 56, 140, 77, 94, 56, 226, 173, 15, 153, 71, 26, 43, 62, 59, 30, 209, 142, 51, 133, 125, 70, 124, 89, 41, 142, 146, 238, 179, 240, 138, 182, 65, 181, 34, 93, 198, 203, 195, 198, 215, 186, 6, 125, 148, 95, 122, 62, 216, 48, 222, 71, 47, 147, 87, 63, 64, 53, 133, 124, 243, 144, 144, 22, 148, 149, 216, 60, 33, 40, 71, 50, 128, 51, 16, 238, 107, 221, 131, 182, 179, 9, 53, 63, 32, 128, 172, 156, 152, 69, 235, 128, 32, 123, 198, 242, 79, 69, 124, 99, 203, 160, 163, 177, 36, 136, 144, 168, 134, 162, 28, 169, 185, 168, 169, 230, 122, 59, 167, 7, 133, 216, 231, 199, 214, 69, 4, 227, 60, 150, 130, 247, 67, 137, 47, 176, 240, 84, 208, 191, 185, 15, 158, 157, 200, 131, 119, 8, 194, 239, 138, 240, 201, 105, 84, 80, 81, 35, 159, 240, 224, 86, 137, 201, 123, 250, 34, 196, 111, 8, 25, 119, 11, 251, 222, 89, 152, 85, 156, 74, 132, 57, 90, 5, 100, 58, 205, 233, 88, 123, 199, 236, 193, 162, 22, 149, 106, 146, 93, 36, 89, 172, 29, 110, 117, 15, 139, 126, 99, 53, 59, 34, 203, 187, 233, 179, 157, 56, 245, 55, 174, 172, 169, 175, 250, 50, 85, 28, 29, 40, 69, 24, 99, 33, 31, 180, 85, 254, 111, 18, 247, 114, 22, 82, 142, 185, 91, 103, 130, 215, 247, 231, 139, 163, 176, 101, 93, 233, 195, 87, 24, 255, 30, 107, 12, 93, 163, 81, 109, 92, 24, 195, 234, 15, 177, 208, 140, 109, 135, 250, 72, 216, 47, 120, 192, 240, 9, 173, 109, 157, 10, 95, 0, 23, 29, 216, 80, 147, 235, 197, 107, 221, 145, 75, 170, 211, 124, 115, 23, 53, 74, 12, 107, 157, 9, 194, 92, 230, 172, 98, 31, 189, 158, 13, 51, 165, 16, 129, 208, 231, 158, 18, 232, 1, 3, 238, 16, 255, 255, 218, 90, 132, 79, 249, 151, 248, 74, 6, 67, 105, 109, 122, 135, 5, 237, 201, 240, 211, 229, 104, 224, 172, 2, 108, 141, 29, 96, 177, 232, 217, 148, 153, 180, 243, 43, 43, 64, 11, 171, 13, 137, 50, 57, 161, 138, 136, 193, 163, 225, 30, 89, 124, 55, 111, 95, 13, 87, 90, 61, 92, 107, 116, 223, 193, 148, 234, 246, 191, 62, 25, 216, 81, 227, 13, 162, 48, 232, 92, 35, 128, 239, 118, 127, 168, 230, 242, 15, 148, 119, 239, 249, 95, 120, 130, 107, 161, 184, 93, 228, 91, 150, 103, 197, 173, 175, 186, 82, 120, 210, 146, 139, 224, 185, 111, 23, 234, 91, 33, 15, 201, 241, 248, 45, 58, 25, 128, 169, 194, 225, 141, 170, 46, 236, 27, 120, 209, 191, 174, 109, 228, 213, 238, 40, 129, 142, 236, 1, 156, 36, 194, 238, 241, 135, 19, 157, 134, 57, 13, 96, 195, 243, 182, 168, 180, 224, 55, 162, 143, 149, 140, 167, 36, 252, 58, 102, 62, 233, 70, 251, 109, 145, 24, 0, 92, 202, 67, 98, 0, 11, 56, 102, 248, 251, 0, 99, 101, 61, 179, 196, 232, 205, 195, 205, 123, 137, 249, 169, 13, 3, 76, 132, 230, 149, 212, 226, 212, 121, 62, 139, 179, 72, 50, 50, 28, 105, 236, 90, 216, 254, 112, 125, 48, 17, 185, 217, 98, 154, 198, 64, 83, 218, 139, 155, 107, 141, 202, 96, 20, 98, 118, 45, 28, 47, 233, 148, 224, 181, 24, 173, 235, 60, 28, 167, 32, 105, 22, 152, 27, 54, 181, 147, 243, 20, 35, 235, 253, 12, 104, 106, 51, 245, 125, 92, 108, 184, 115, 33, 177, 28, 248, 239, 219, 226, 226, 144, 29, 68, 228, 132, 241, 113, 141, 152, 89, 36, 84, 78, 229, 218, 47, 172, 119, 217, 6, 156, 58, 168, 226, 247, 232, 9, 68, 49, 178, 67, 94, 135, 246, 149, 244, 92, 232, 157, 144, 113, 193, 239, 99, 177, 238, 144, 61, 184, 220, 237, 155, 0, 253, 42, 149, 20, 164, 194, 209, 115, 122, 191, 86, 176, 172, 124, 129, 233, 99, 230, 47, 160, 167, 104, 113, 100, 65, 48, 196, 179, 132, 237, 174, 184, 253, 86, 5, 118, 212, 211, 243, 86, 230, 142, 223, 160, 190, 209, 235, 185, 38, 183, 75, 33, 49, 137, 197, 23, 13, 110, 9, 61, 210, 125, 6, 131, 42, 130, 86, 70, 92, 1, 138, 242, 206, 54, 36, 27, 90, 113, 179, 121, 79, 247, 228, 249, 4, 32, 43, 231, 17, 190, 120, 76, 36, 111, 245, 76, 46, 146, 230, 187, 43, 37, 7, 57, 123, 7, 20, 237, 38, 133, 236, 241, 202, 155, 220, 243, 184, 199, 60, 147, 48, 187, 102, 58, 138, 42, 196, 171, 124, 79, 226, 86, 36, 97, 183, 107, 157, 121, 176, 97, 199, 232, 11, 72, 132, 51, 79, 45, 104, 30, 121, 180, 104, 74, 159, 195, 112, 144, 96, 223, 182, 61, 101, 161, 43, 223, 216, 91, 103, 199, 145, 5, 254, 202, 102, 125, 181, 129, 243, 29, 182, 12, 218, 169, 158, 143, 40, 49, 129, 11, 57, 214, 102, 111, 115, 249, 188, 38, 114, 79, 96, 72, 138, 159, 35, 31, 227, 135, 91, 46, 61, 47, 1, 199, 155, 162, 107, 154, 173, 201, 37, 181, 228, 200, 51, 126, 64, 46, 138, 185, 80, 255, 61, 172, 0, 249, 48, 157, 179, 149, 221, 141, 138, 123, 80, 37, 188, 116, 94, 106, 73, 148, 122, 93, 126, 46, 143, 145, 15, 249, 87, 208, 39, 251, 32, 138, 86, 228, 157, 36, 74, 102, 182, 93, 169, 147, 159, 38, 252, 135, 242, 129, 149, 139, 179, 101, 208, 65, 184, 11, 70, 161, 185, 204, 6, 119, 252, 170, 135, 220, 96, 149, 186, 244, 143, 87, 10, 177, 251, 244, 254, 210, 101, 230, 104, 120, 154, 206, 251, 202, 163, 65, 135, 193, 128, 101, 66, 139, 64, 1, 126, 4, 180, 29, 39, 136, 190, 218, 126, 1, 253, 216, 251, 251, 136, 133, 167, 79, 219, 185, 3, 118, 189, 240, 245, 64, 159, 186, 169, 99, 151, 77, 121, 133, 150, 134, 24, 48, 168, 252, 2, 187, 15, 25, 220, 244, 57, 33, 230, 212, 94, 200, 29, 20, 181, 24, 88, 156, 232, 4, 241, 42, 49, 22, 39, 249, 150, 48, 70, 33, 182, 34, 205, 8, 128, 79, 82, 43, 37, 68, 151, 36, 45, 183, 137, 77, 165, 240, 87, 75, 152, 250, 212, 86, 160, 35, 171, 95, 63, 106, 108, 141, 165, 157, 187, 172, 115, 3, 35, 109, 189, 207, 166, 176, 161, 104, 227, 225, 210, 41, 50, 251, 153, 251, 162, 80, 251, 118, 136, 40, 125, 86, 177, 142, 59, 106, 109, 227, 207, 169, 149, 170, 37, 66, 231, 140, 234, 23, 108, 34, 113, 69, 8, 199, 3, 170, 228, 168, 234, 55, 11, 219, 95, 65, 115, 182, 89, 54, 200, 168, 58, 16, 15, 141, 162, 192, 26, 111, 36, 152, 171, 228, 116, 45, 32, 111, 250, 224, 16, 254, 0, 129, 8, 194, 45, 200, 51, 209, 68, 143, 229, 184, 187, 91, 225, 163, 53, 10, 51, 33, 33, 207, 64, 177, 107, 143, 102, 102, 252, 132, 44, 217, 54, 166, 249, 36, 99, 55, 136, 194, 87, 76, 1, 10, 86, 17, 183, 74, 235, 97, 165, 125, 197, 139, 2, 31, 178, 187, 124, 228, 234, 108, 145, 81, 52, 135, 35, 163, 1, 238, 205, 131, 13, 218, 38, 7, 0, 48, 127, 238, 246, 151, 21, 124, 246, 64, 131, 247, 53, 174, 179, 77, 242, 110, 165, 71, 21, 24, 149, 188, 147, 163, 117, 234, 95, 72, 203, 52, 63, 220, 120, 133, 75, 20, 37, 246, 42, 147, 15, 52, 242, 152, 243, 135, 153, 148, 34, 196, 241, 162, 38, 75, 122, 175, 186, 202, 185, 6, 190, 248, 80, 95, 90, 242, 56, 173, 208, 69, 245, 178, 94, 133, 211, 151, 249, 6, 185, 233, 253, 67, 72, 114, 14, 13, 214, 98, 234, 247, 183, 174, 43, 226, 138, 205, 216, 78, 113, 12, 234, 5, 127, 169, 168, 226, 66, 227, 142, 179, 224, 190, 7, 25, 244, 162, 221, 88, 61, 230, 198, 174, 108, 132, 116, 207, 18, 157, 69, 126, 100, 199, 33, 168, 56, 211, 125, 167, 180, 123, 255, 93, 230, 229, 201, 79, 177, 196, 206, 88, 28, 34, 120, 112, 164, 115, 25, 37, 140, 179, 231, 178, 33, 45, 229, 184, 165, 231, 98, 53, 151, 40, 68, 50, 252, 88, 249, 156, 230, 203, 120, 200, 126, 189, 244, 58, 242, 148, 145, 159, 221, 243, 79, 84, 84, 0, 182, 30, 26, 188, 221, 132, 250, 66, 197, 110, 100, 40, 252, 81, 238, 234, 121, 62, 47, 75, 132, 180, 210, 166, 233, 158, 233, 184, 152, 3, 171, 6, 155, 95, 239, 248] }, final_destination_options: Some(Ipv6RawExtHeader { next_header: 17 (UDP - User Datagram), payload: [6, 183, 85, 108, 29, 243, 154, 128, 29, 59, 89, 238, 74, 123, 133, 40, 131, 86, 47, 110, 138, 68, 159, 94, 97, 94, 63, 139, 37, 185, 26, 203, 163, 25, 119, 198, 221, 212, 172, 176, 157, 124, 33, 6, 210, 49, 91, 176, 109, 232, 194, 87, 149, 58, 127, 22, 24, 41, 247, 31, 249, 191, 159, 173, 5, 241, 71, 58, 71, 32, 109, 149, 93, 229, 140, 69, 28, 23, 126, 120, 19, 4, 198, 253, 47, 49, 121, 98, 125, 136, 39, 245, 107, 219, 238, 180, 238, 109, 105, 171, 170, 187, 158, 99, 66, 162, 169, 38, 72, 158, 76, 114, 158, 182, 85, 90, 64, 87, 50, 67, 174, 63, 112, 225, 254, 19, 150, 162, 77, 36, 11, 113, 170, 221, 105, 137, 206, 55, 54, 216, 248, 159, 194, 223, 166, 87, 175, 125, 66, 92, 88, 245, 224, 215, 107, 39, 145, 114, 104, 127, 147, 2, 78, 225, 112, 232, 182, 79, 98, 91, 156, 19, 172, 244, 106, 205, 133, 210, 11, 56, 37, 248, 216, 88, 241, 212, 122, 127, 13, 123, 6, 64, 182, 114, 27, 185, 202, 16, 170, 241, 74, 28, 175, 35, 188, 204, 90, 163, 183, 184, 136, 99, 206, 224, 245, 209, 11, 198, 187, 235, 24, 46, 118, 102, 83, 33, 224, 202, 204, 32, 186, 148, 164, 255, 205, 223, 102, 81, 118, 90, 159, 90, 114, 103, 211, 186, 36, 101, 95, 52, 215, 48, 108, 136, 179, 63, 123, 73, 55, 187, 102, 50, 10, 114, 105, 88, 102, 42, 190, 163, 48, 50, 34, 192, 126, 125, 74, 71, 135, 189, 18, 241, 183, 151, 2, 82, 204, 76, 56, 5, 121, 80, 9, 134, 44, 206, 104, 237, 130, 150, 6, 23, 175, 3, 32, 137, 164, 14, 36, 67, 96, 75, 107, 53, 123, 237, 226, 226, 181, 57, 240, 203, 108, 42, 29, 47, 31, 143, 122, 129, 172, 246, 157, 121, 247, 213, 34, 197, 191, 73, 2, 36, 171, 153, 229, 123, 44, 32, 6, 254, 116, 130, 151, 151, 173, 247, 211, 41, 6, 83, 1, 238, 138, 149, 79, 160, 156, 110, 226, 185, 184, 155, 39, 71, 158, 123, 23, 244, 140, 169, 51, 141, 11, 210, 215, 91, 30, 103, 45, 172, 222, 87, 115, 233, 19, 52, 96, 150, 186, 173, 138, 249, 241, 132, 103, 48, 66, 90, 52, 67, 255, 193, 26, 247, 141, 209, 19, 67, 56, 33, 210, 29, 76, 1, 48, 250, 31, 45, 41, 182, 133, 94, 120, 147, 92, 60, 159, 211, 88, 7, 174, 195, 233, 221, 149, 39, 105, 47, 201, 252, 49, 119, 87, 145, 192, 207, 1, 194, 98, 90, 247, 103, 197, 88, 7, 87, 127, 163, 111, 122, 132, 54, 111, 208, 157, 209, 172, 66, 88, 132, 241, 8, 41, 59, 169, 168, 45, 196, 185, 64, 20, 44, 158, 37, 182, 215, 78, 171, 253, 206, 250, 109, 117, 123, 212, 89, 59, 178, 155, 159, 159, 75, 247, 184, 102, 191, 87, 217, 19, 64, 159, 30, 153, 67, 44, 152, 189, 42, 75, 44, 227, 194, 75, 179, 52, 50, 76, 211, 183, 245, 187, 164, 203, 84, 37, 89, 56, 176, 78, 240, 224, 18, 131, 191, 37, 248, 181, 126, 73, 218, 64, 149, 155, 42, 159, 135, 36, 217, 118, 52, 148, 245, 158, 8, 6, 189, 47, 100, 193, 230, 93, 62, 172, 165, 28, 214, 69, 210, 107, 246, 6, 89, 28, 220, 16, 176, 16, 155, 192, 69, 128, 81, 148, 92, 78, 215, 131, 78, 158, 140, 11, 217, 194, 37, 55, 20, 177, 92, 166, 74, 218, 32, 218, 112, 165, 58, 220, 123, 232, 213, 6, 246, 199, 249, 118, 222, 156, 231, 35, 108, 100, 120, 12, 170, 14, 120, 248, 220, 61, 114, 211, 181, 88, 44, 253, 177, 36, 162, 150, 220, 54, 62, 42, 18, 191, 100, 247, 49, 85, 18, 239, 15, 249, 8, 214, 187, 7, 236, 152, 107, 92, 1, 159, 153, 95, 51, 122, 170, 169, 181, 92, 248, 119, 77, 35, 230, 14, 70, 180, 107, 92, 115, 116, 162, 149, 9, 189, 142, 100, 187, 226, 227, 213, 95, 19, 149, 101, 164, 154, 249, 11, 203, 134, 6, 175, 207, 160, 175, 255, 1, 14, 164, 79, 138, 220, 140, 193, 212, 210, 248, 185, 158, 254, 70, 36, 138, 111, 182, 213, 66, 30, 171, 170, 33, 13, 150, 198, 242, 16, 213, 126, 173, 76, 232, 38, 23, 46, 106, 173, 174, 233, 53, 158, 197, 97, 249, 105, 216, 138, 121, 121, 198, 30, 117, 111, 222, 168, 58, 92, 149, 55, 199, 121, 18, 216, 209, 28, 153, 184, 157, 78, 22, 162, 52, 253, 219, 70, 117, 180, 230, 172, 54, 100, 73, 90, 21, 243, 12, 161, 10, 49, 120, 231, 240, 189, 184, 49, 250, 106, 210, 64, 173, 67, 115, 179, 146, 80, 124, 29, 92, 81, 49, 167, 147, 105, 8, 232, 4, 35, 207, 240, 145, 223, 21, 252, 212, 159, 1, 226, 46, 182, 232, 167, 184, 163, 246, 91, 158, 190, 141, 189, 125, 253, 69, 140, 88, 69, 250, 244, 57, 208, 13, 149, 204, 26, 150, 194, 167, 183, 237, 97, 208, 197, 215, 216, 63, 55, 139, 121, 31, 59, 187, 108, 200, 147, 85, 34, 96, 114, 231, 97, 20, 4, 219, 156, 1, 228, 102, 8, 149, 99, 30, 210, 224, 92, 3, 66, 188, 245, 210, 162, 204, 51, 58, 110, 173, 44, 174, 2, 101, 156, 132, 91, 67, 128, 227, 63, 78, 69, 193, 19, 88, 248, 182, 169, 101, 214, 6, 196, 90, 224, 168, 94, 162, 124, 82, 59, 231, 76, 191, 113, 131, 224, 141, 26, 138, 93, 72, 111, 131, 35, 8, 136, 33, 107, 236, 56, 254, 62, 35, 96, 94, 204, 211, 88, 121, 198, 81, 116, 10, 167, 161, 52, 109, 60, 9, 57, 22, 192, 214, 40, 174, 243, 252, 237, 9, 249, 33, 18, 229, 175, 174, 118, 13, 211, 21, 129, 122, 5, 77, 189, 104, 153, 4, 219, 179, 113, 174, 245, 233, 91, 170, 11, 79, 176, 166, 146, 124, 15, 248, 188, 100, 108, 21, 116, 127, 180, 210, 74, 176, 197, 253, 183, 80, 95, 41, 212, 130, 187, 252, 222, 179, 197, 35, 153, 95, 222, 207, 113, 148, 116, 250, 220, 108, 116, 217, 38, 117, 57, 77, 193, 225, 82, 217, 143, 90, 207, 140, 155, 57, 37, 115, 142, 3, 35, 31, 208, 163, 41, 217, 47, 92, 166, 146, 32, 147, 137, 153, 77, 115, 147, 4, 75, 90, 54, 62, 13, 136, 169, 226, 61, 249, 140, 211, 187, 252, 212, 112, 203, 217, 207, 240, 170, 176, 123, 96, 146, 119, 7, 255, 133, 178, 159, 129, 250, 254, 252, 176, 57, 250, 2, 19, 212, 143, 99, 24, 247, 33, 220, 2, 39, 63, 163, 180, 44, 122, 142, 166, 194, 19, 248, 187, 129, 108, 159, 89, 228, 56, 196, 95, 150, 42, 182, 150, 131, 189, 14, 66, 200, 254, 117, 27, 52, 100, 108, 168, 211, 131, 67, 51, 192, 211, 244, 185, 250, 49, 209, 195, 61, 66, 87, 207, 146, 53, 182, 155, 179, 55, 25, 182, 213, 85, 111, 55, 97, 71, 35, 108, 84, 120, 94, 32, 67, 208, 119, 178, 195, 160, 102, 10, 23, 155, 249, 173, 175, 60, 2, 32, 189, 21, 211, 30, 125, 70, 180, 141, 39, 40, 113, 187, 234, 250, 204, 137, 209, 185, 19, 103, 3, 111, 135, 120, 232, 133, 141, 159, 115, 136, 8, 191, 165, 60, 82, 174, 122, 19, 65, 62, 112, 234, 162, 13, 71, 12, 12, 102, 217, 227, 171, 128, 29, 217, 118, 31, 50, 51, 209, 159, 16, 218, 234, 175, 2, 175, 64, 62, 147, 238, 166, 104, 190, 65, 16, 48, 47, 155, 58, 223, 19, 46, 140, 69, 124, 103, 146, 153, 160, 253, 215, 137, 49, 176, 238, 85, 4, 225, 243, 253, 6, 24, 118, 122, 7, 141, 41, 115, 46, 114, 22, 91, 133, 9, 118, 207, 138, 138, 218, 222, 182, 55, 12, 8, 173, 83, 136, 232, 136, 173, 180, 253, 12, 254, 234, 54, 250, 217, 10, 186, 183, 120, 80, 33, 26, 148, 163, 36, 37, 1, 227, 101, 95, 19, 193, 198, 181, 192, 253, 182, 223, 74, 207, 68, 160, 180, 186, 118, 51, 184, 255, 200, 109, 235, 252, 137, 166, 138, 0, 98, 36, 44, 157, 142, 215, 205, 171, 118, 241, 198, 229, 159, 85, 31, 165, 28, 59, 165, 206, 188, 125, 86, 195, 148, 207, 75, 199, 43, 224, 223, 123, 67, 127, 126, 230, 141, 107, 205, 55, 237, 201, 194, 217, 27, 235, 102, 25, 10, 164, 131, 218, 233, 207, 108, 73, 76, 101, 8, 222, 207, 128, 168, 18, 89, 242, 115, 251, 106, 11, 160, 208, 98, 48, 181, 253, 165, 251, 40, 39, 147, 65, 63, 75, 30, 192, 94, 168, 237, 34, 64, 21, 206, 15, 125, 87, 185, 1, 134, 179, 134, 248, 190, 11, 189, 185, 220, 254, 147, 108, 22, 216, 58, 91, 52, 210, 169, 29, 144, 210, 239, 42, 65, 29, 217, 89, 87, 178, 244, 159, 168, 232, 76, 87, 183, 63, 150, 36, 39, 134, 214, 227, 129, 111, 174, 245, 18, 90, 63, 224, 246, 223, 108, 126, 169, 233, 2, 159, 169, 156, 20, 5, 25, 35, 178, 58, 4, 152, 195, 100, 103, 20, 48, 133, 119, 44, 189, 149, 49, 208, 82, 237, 219, 213, 166, 130, 99, 251, 120, 151, 164, 170, 102, 62, 202, 222, 23, 209, 134, 128, 15, 102, 118, 145, 231, 144, 149, 79, 152, 52, 223, 141, 20, 128, 140, 21, 195, 9, 48, 180, 232, 225, 46, 119, 138, 59, 128, 137, 141, 172, 14, 39, 187, 121, 85, 208, 115, 81, 56, 171, 23, 212, 86, 191, 195, 141, 213, 175, 22, 170, 99, 103, 54, 95, 168, 168, 214, 50, 241, 95, 24, 113, 76, 229, 83, 84, 219, 13, 193, 141, 252, 150, 71, 180, 73, 141, 77, 241, 199, 138, 154, 6, 45, 224, 182, 74, 182, 11, 230, 80, 14, 209, 131, 134, 166, 111, 197, 69, 205, 110, 198, 39, 108, 248, 115, 115, 95, 87, 209, 183, 89, 50, 80, 184, 191, 166, 76, 76, 205, 148, 78, 38, 82, 56, 158, 81, 68, 87, 170, 16, 251, 50, 15, 240, 34, 231, 214, 234, 143, 52, 46, 126, 122, 184, 102, 39, 4, 27, 246, 218, 204, 27, 116, 137, 175, 65, 90, 240, 118, 188, 18, 197, 138, 77, 117, 37, 244, 103, 126, 174, 181, 228, 59, 195, 101, 92, 187, 128, 218, 59, 79, 148, 18, 250, 86, 241, 80, 153, 198, 238, 96, 6, 253, 235, 38, 151, 54, 176, 110, 195, 156, 92, 60, 143, 174, 139, 89, 69, 144, 228, 47, 190, 45, 138, 202, 222, 144, 109, 122, 214, 196, 162, 255, 219, 64, 116, 238, 225, 242, 19, 166, 15, 118, 53, 57, 103, 116, 19, 42, 179, 181, 175, 88, 22, 86, 109, 101, 176, 57, 189, 131, 173, 165, 77, 227, 191, 77, 202, 217, 56, 130, 218, 12, 184, 221, 230, 138, 169, 208, 63, 62, 42, 64, 201, 250, 50, 158, 159, 100, 38, 140, 253, 19, 176, 66, 195, 62, 30, 121, 122, 152, 204, 5, 197, 207, 217, 177, 152, 90, 242, 215, 64, 15, 189, 149, 252, 102, 224, 77, 147, 243, 216, 31, 163, 200, 31, 7, 82, 245, 182, 118, 120, 172, 130, 44, 224, 130, 210, 122] }) }), fragment: Some(Ipv6FragmentHeader { next_header: 51 (AH - Authentication Header), fragment_offset: IpFragOffset(1102), more_fragments: true, identification: 1036890713 }), auth: Some(IpAuthHeader { next_header: 60 (IPv6-Opts - Destination Options for IPv6), spi: 4019373455, sequence_number: 367045836, raw_icv: [98, 56, 186, 79, 65, 110, 3, 30, 62, 183, 253, 171, 45, 41, 28, 5, 207, 15, 211, 247, 109, 98, 166, 132, 109, 117, 52, 89, 80, 46, 178, 17, 76, 44, 18, 10, 233, 40, 225, 109, 20, 229, 114, 240, 225, 245, 197, 78, 9, 123, 149, 166, 156, 179, 247, 28, 201, 102, 199, 12, 156, 171, 59, 60, 84, 87, 54, 14, 163, 70, 126, 115, 180, 161, 161, 241, 198, 72, 222, 171, 95, 94, 116, 15, 185, 107, 165, 70, 6, 179, 16, 151, 170, 112, 15, 204, 58, 245, 69, 148, 105, 163, 49, 142, 68, 160, 44, 56, 17, 153, 55, 22, 89, 177, 17, 37, 7, 129, 181, 211, 122, 120, 158, 198, 199, 1, 161, 205, 38, 185, 182, 197, 174, 72, 190, 34, 241, 149, 47, 69] }) }
diff --git a/proptest-regressions/internet/ipv4_dscp.txt b/proptest-regressions/internet/ipv4_dscp.txt
new file mode 100644
index 0000000..d0f6f95
--- /dev/null
+++ b/proptest-regressions/internet/ipv4_dscp.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 5b030cccbf007850504f9893ee7f26cdf0897ddc3f98e23a3c337eeee2066467 # shrinks to valid_value = 0, invalid_value = 64
diff --git a/proptest-regressions/internet/ipv4_exts.txt b/proptest-regressions/internet/ipv4_exts.txt
new file mode 100644
index 0000000..b853bfa
--- /dev/null
+++ b/proptest-regressions/internet/ipv4_exts.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 437386ca2a231427893f72679446650276d0b4913d7a8a27c4d792a864806647 # shrinks to auth = IpAuthHeader { next_header: 38 (IDPR-CMTP - IDPR Control Message Transport Proto), spi: 0, sequence_number: 0, raw_icv: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 97, 221, 214, 171, 240, 40, 121, 63, 60, 128, 84, 176, 126, 51, 89, 111, 186, 144, 173, 18, 139, 65, 203, 201, 92, 255, 180, 14, 203, 163, 11, 179, 7, 7, 44, 142, 26, 247, 206, 130, 175, 158, 169, 170, 53, 78, 236, 79, 248, 144, 107, 85, 25, 231, 37, 222, 117, 244, 14, 164, 136, 68, 30, 9, 152, 134, 8, 107, 211, 118, 102, 203, 44, 90, 247, 6, 60, 144, 125, 230, 101, 125, 199, 47, 9, 219, 201, 123, 113, 85, 150, 243, 215, 176, 57, 142, 140, 220, 179, 126, 63, 100, 189, 58, 92, 213, 238, 109, 145, 122, 108, 13, 84, 177, 150, 236, 215, 245, 9, 236, 61, 38, 196, 191, 11, 4, 67, 249, 253, 248, 93, 155, 186, 11, 233, 120, 56, 0, 75, 188, 241, 134, 41, 24, 140, 124, 243, 63, 181, 106, 63, 178, 153, 228, 48, 183, 170, 194, 182, 78, 79, 158, 164, 174, 233, 241, 186, 105, 165, 182, 78, 161, 58, 0, 79, 190, 201, 77, 133, 75, 123, 169, 93, 88, 95, 173, 136, 6, 211, 97, 219, 238, 252, 206, 235, 86, 16, 179, 250, 233, 55, 239, 251, 241, 196, 226, 20, 203, 78, 133, 131, 236, 204, 2, 121, 79, 195, 197, 60, 168, 16, 195, 122, 1, 229, 178, 15, 53, 93, 72, 88, 163, 136, 58, 109, 44, 22, 76, 79, 213, 168, 141, 197, 110, 56, 32, 240, 253, 168, 147, 28, 171, 238, 239, 95, 88, 31, 181, 5, 66, 31, 44, 246, 201, 112, 20, 187, 46, 14, 171, 193, 142, 27, 73, 2, 56, 93, 251, 147, 55, 30, 194, 37, 102, 65, 85, 131, 113, 130, 109, 209, 13, 198, 168, 176, 241, 218, 70, 210, 178, 12, 184, 184, 234, 122, 128, 217, 237, 101, 102, 209, 68, 46, 108, 100, 178, 85, 21, 143, 224, 206, 231, 236, 204, 153, 111, 31, 253, 141, 148, 181, 154, 120, 66, 0, 177, 29, 166, 179, 187, 53, 219, 42, 136, 217, 95, 213, 205, 151, 236, 1, 52, 56, 11, 164, 146, 137, 40, 41, 50, 137, 154, 113, 175, 199, 151, 18, 168, 211, 205, 230, 131, 171, 214, 157, 21, 228, 116, 32, 91, 216, 181, 48, 223, 154, 94, 146, 193, 114, 113, 44, 212, 98, 69, 134, 119, 74, 173, 173, 119, 49, 130, 23, 28, 207, 167, 180, 86, 13, 109, 36, 84, 194, 113, 36, 33, 161, 221, 172, 19, 75, 16, 108, 51, 255, 118, 74, 101, 172, 238, 105, 45, 12, 101, 28, 108, 77, 234, 251, 196, 233, 187, 40, 176, 220, 4, 59, 128, 48, 95, 49, 123, 37, 224, 62, 36, 217, 103, 12, 152, 162, 26, 49, 148, 82, 50, 63, 64, 7, 102, 185, 45, 71, 41, 184, 55, 195, 172, 80, 217, 251, 38, 5, 194, 139, 67, 61, 160, 52, 15, 173, 116, 153, 111, 222, 228, 145, 193, 14, 134, 6, 28, 66, 148, 163, 111, 107, 15, 94, 157, 48, 154, 36, 145, 41, 15, 3, 7, 208, 168, 153, 239, 109, 16, 117, 90, 254, 130, 187, 186, 72, 40, 194, 27, 25, 115, 31, 248, 20, 19, 145, 123, 27, 39, 30, 141, 189, 30, 229, 102, 208, 174, 249, 221, 170, 241, 156, 193, 138, 20, 26, 65, 228, 31, 99, 163, 213, 50, 225, 17, 227, 96, 47, 136, 8, 118, 176, 216, 181, 24, 32, 139, 41, 78, 179, 212, 101, 15, 124, 188, 33, 86, 175, 129, 121, 246, 11, 25, 121, 184, 144, 12, 108, 177, 9, 113, 108, 82, 214, 207, 215, 118, 239, 129, 56] }
diff --git a/proptest-regressions/internet/ipv4_header.txt b/proptest-regressions/internet/ipv4_header.txt
new file mode 100644
index 0000000..60af61a
--- /dev/null
+++ b/proptest-regressions/internet/ipv4_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 1873fa00d9b4f0af87861f075c5ba29e74d7dac118637e4c1a33d41a3009a28f # shrinks to ref base_header = 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: 145, header_checksum: 0, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [0, 0, 0, 0, 0, 0, 0, 0] }
+cc 13666c027d7ea3b1272e605857d2a4f6532e92dd8509f6003e9b2124cdb0387e # shrinks to header = Ipv4Header { ihl: 5, 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: 233, header_checksum: 0, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [] }
+cc f85bc62081cbee15e8029eb6aca54a2495c572e0652775898c1309c17cda3b95 # shrinks to base_header = Ipv4Header { ihl: 8, 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: 127, header_checksum: 0, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 85] }, bad_dscp = 169, bad_ecn = 48, bad_frag_offset = 7337
diff --git a/proptest-regressions/internet/ipv4_header_slice.txt b/proptest-regressions/internet/ipv4_header_slice.txt
new file mode 100644
index 0000000..3c255ec
--- /dev/null
+++ b/proptest-regressions/internet/ipv4_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 4cc24fab1e703ab4a8d4563e9d3f4ca100aa7611386914d66993eb37d7c3fcd6 # shrinks to header = Ipv4Header { ihl: 6, 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: 252, header_checksum: 0, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [0, 0, 0, 0] }
diff --git a/proptest-regressions/internet/ipv4_slice.txt b/proptest-regressions/internet/ipv4_slice.txt
new file mode 100644
index 0000000..d04dd07
--- /dev/null
+++ b/proptest-regressions/internet/ipv4_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 62df519c8bf39483cdc21b9435ada07bcbc0c93e3b5cfde3f625462f7c921af1 # shrinks to ipv4_base = 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: 72, header_checksum: 0, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [0, 0, 0, 0, 0, 0, 0, 0] }, auth = IpAuthHeader { next_header: 201, spi: 691902600, sequence_number: 1746395289, raw_icv: [99, 64, 135, 43, 176, 74, 16, 119, 210, 136, 36, 126, 163, 103, 81, 170, 173, 25, 151, 100, 205, 249, 216, 29, 116, 80, 24, 86, 50, 27, 194, 236, 158, 73, 167, 118, 123, 117, 186, 180, 117, 192, 96, 88, 212, 124, 3, 192, 212, 124, 83, 141, 192, 144, 204, 23, 215, 74, 216, 224, 201, 177, 218, 4, 0, 65, 196, 79, 95, 171, 218, 248, 142, 182, 204, 47, 127, 220, 51, 163, 56, 170, 131, 138, 25, 153, 204, 0, 218, 10, 3, 207, 71, 30, 214, 190, 140, 34, 230, 20, 254, 72, 225, 233, 243, 164, 26, 42, 118, 244, 169, 111, 87, 13, 99, 243, 179, 212, 183, 14, 233, 136, 236, 211, 252, 112, 82, 8, 175, 124, 167, 139, 255, 190, 21, 175, 146, 217, 253, 133, 220, 183, 101, 59, 26, 211, 127, 150, 219, 149, 129, 138, 50, 218, 153, 249, 82, 129, 165, 211, 79, 14, 182, 187, 240, 94, 13, 32, 220, 241, 2, 97, 143, 62, 45, 233, 91, 120, 170, 6, 149, 235, 74, 39, 80, 127, 239, 114, 46, 74, 199, 12, 69, 18, 113, 160, 223, 34, 213, 20, 128, 203, 202, 121, 86, 82, 48, 29, 17, 4, 102, 115, 101, 113, 234, 201, 88, 95, 113, 185, 101, 184, 4, 105, 195, 72, 99, 153, 158, 88, 44, 171, 222, 63, 46, 125, 32, 136, 20, 255, 149, 4, 188, 143, 183, 57, 217, 67, 235, 239, 195, 171, 131, 184, 85, 6, 216, 253, 36, 83, 107, 205, 76, 188, 68, 88, 188, 84, 9, 211, 247, 158, 136, 87, 163, 238, 110, 252, 125, 60, 242, 241, 58, 118, 119, 5, 38, 11, 239, 76, 35, 77, 9, 12, 149, 67, 186, 133, 181, 48, 56, 150, 54, 147, 134, 69, 46, 32, 199, 192, 97, 105, 123, 72, 31, 199, 107, 247, 108, 180, 72, 210, 103, 116, 87, 89, 227, 249, 113, 14, 62, 132, 206, 34, 130, 169, 200, 100, 22, 53, 20, 148, 248, 153, 138, 56, 70, 192, 211, 240, 109, 187, 158, 76, 112, 236, 30, 86, 77, 34, 186, 159, 134, 197, 32, 63, 35, 228, 159, 229, 248, 218, 9, 0, 178, 56, 98, 110, 46, 73, 150, 211, 96, 202, 20, 40, 233, 66, 140, 174, 48, 40, 136, 91, 95, 243, 153, 114, 5, 252, 5, 17, 75, 158, 130, 7, 232, 192, 133, 128, 40, 96, 9, 71, 246, 108, 242, 188, 88, 108, 130, 186, 239, 99, 6, 85, 108, 0, 157, 227, 96, 255, 255, 131, 34, 30, 116, 176, 92, 189, 231, 63, 235, 140, 219, 214, 237, 48, 51, 162, 7, 150, 190, 169, 104, 55, 134, 196, 72, 217, 82, 31, 47, 127, 153, 150, 168, 55, 210, 238, 87, 94, 113, 178, 144, 82, 17, 198, 215, 83, 179, 164, 127, 77, 118, 15, 156, 94, 251, 144, 169, 150, 190, 87, 248, 22, 31, 56, 173, 140, 201, 235, 126, 13, 232, 166, 76, 14, 127, 15, 36, 59, 247, 99, 122, 42, 15, 35, 75, 186, 176, 206, 10, 129, 101, 112, 131, 79, 141, 130, 88, 189, 156, 115, 84, 211, 147, 185, 127, 95, 8, 128, 213, 132, 232, 55, 133, 15, 248, 56, 113, 83, 85, 146, 192, 210, 80, 129, 94, 165, 99, 8, 44, 89, 187, 180, 133, 1, 204, 148, 213, 170, 91, 195, 123, 29, 244, 160, 129, 253, 23, 82, 153, 88, 4, 134, 1, 33, 188, 102, 18, 139, 24, 179, 68, 41, 160, 59, 83, 68, 34, 7, 134, 116, 103, 221, 73, 50, 40, 10, 123, 154, 139, 131, 234, 173, 0, 33, 172, 151, 136, 61, 87, 236, 143, 198, 203, 31, 81, 24, 205, 218, 11, 17, 171, 157, 208, 240, 197, 113, 128, 141, 9, 37, 51, 216, 142, 29, 73, 227, 118, 62, 140, 231, 239, 238, 187, 57, 189, 180, 196, 153, 134, 195, 176, 28, 112, 184, 209, 38, 53, 162, 187, 182, 44, 122, 70, 95, 240, 155, 69, 16, 86, 61, 4, 35, 213, 164, 119, 220, 138, 106, 227, 80, 135, 204, 253, 104, 97, 129, 220, 234, 235, 124, 234, 64, 10, 39, 14, 124, 236, 62, 12, 41, 18, 60, 244, 233, 34, 212, 47, 131, 130, 203, 125, 214, 92, 169, 215, 6, 36, 73, 71, 64, 202, 165, 161, 39, 45, 114, 185, 248, 162, 71, 51, 127, 234, 8] }
diff --git a/proptest-regressions/internet/ipv6_exts.txt b/proptest-regressions/internet/ipv6_exts.txt
new file mode 100644
index 0000000..86eeabc
--- /dev/null
+++ b/proptest-regressions/internet/ipv6_exts.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 8241e4aed6355dae62ebffd014e532bc315a4795977336d48b8ed9b83ae04d6f # shrinks to header_size = 0, post_header = 1 (ICMP - Internet Control Message)
diff --git a/proptest-regressions/internet/ipv6_exts_slice.txt b/proptest-regressions/internet/ipv6_exts_slice.txt
new file mode 100644
index 0000000..322a559
--- /dev/null
+++ b/proptest-regressions/internet/ipv6_exts_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 925b9a2a0ec5a0f366aebbadd60534e57feb06ff69477bf7d18e5fa85168aee3 # shrinks to header_size = 0, post_header = 52 (I-NLSP - Integrated Net Layer Security  TUBA)
diff --git a/proptest-regressions/internet/ipv6_fragment_header.txt b/proptest-regressions/internet/ipv6_fragment_header.txt
new file mode 100644
index 0000000..acfcdb2
--- /dev/null
+++ b/proptest-regressions/internet/ipv6_fragment_header.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 69bfd2a19e0bfc33810009e0e3aa8266194847913d0bab87102f5864cf620d56 # shrinks to input = Ipv6FragmentHeader { next_header: 0 (HOPOPT - IPv6 Hop-by-Hop Option), fragment_offset: 0, more_fragments: false, identification: 0 }
diff --git a/proptest-regressions/internet/ipv6_header.txt b/proptest-regressions/internet/ipv6_header.txt
new file mode 100644
index 0000000..d9d6bb3
--- /dev/null
+++ b/proptest-regressions/internet/ipv6_header.txt
@@ -0,0 +1,11 @@
+# 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 ba191f9eb0992f9200fe37591825982662bebdf9bfc7b2a7bed0d89164963203 # shrinks to header = Ipv6Header { traffic_class: 0, flow_label: 0, payload_length: 0, next_header: 18, hop_limit: 18, source: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }
+cc 59af734141c7dae8a4d374101eb96f0bbadaec569d75fb0d299751370440eaa4 # shrinks to header = Ipv6Header { traffic_class: 0, flow_label: 833280, payload_length: 22511, next_header: 10, hop_limit: 89, source: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }
+cc ccf80d2b0764306e4c4e18dfea53a805d33b004db0781460b369431ed2ef4656 # shrinks to header = Ipv6Header { traffic_class: 0, flow_label: 0, payload_length: 0, next_header: 0, hop_limit: 0, source: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }
+cc f0bb947faa64f3cb20fa44eb4e207c8e98a75b996e43d4302459bd16b4331733 # shrinks to header = Ipv6Header { traffic_class: 0, flow_label: 191688, payload_length: 65357, next_header: 11, hop_limit: 85, source: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }
+cc 5159bb8c3c47d77a24cb36c98bac2cc43a26e087841764055e84f5f6797a3908 # shrinks to hop_by_hop = Ipv6RawExtHeader { next_header: 90, payload: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 136, 129, 150, 54, 179, 159, 111, 199, 45, 49, 48, 108, 29, 177, 15, 177, 180, 221, 132, 62, 252, 205, 180, 34, 229, 70, 230, 36, 126, 240, 12, 78, 197, 179, 135, 39, 141, 207, 234, 180, 53, 74, 8, 99, 94, 183, 171, 147, 222, 168, 187, 234, 18, 124, 172, 15, 179, 186, 209, 196, 42, 143, 15, 130, 223, 242, 185, 92, 241, 220, 186, 146, 139, 210, 14, 188, 191, 46, 7, 124, 132, 112, 211, 127, 201, 102, 122, 109, 93, 192, 26, 13, 224, 50, 7, 210, 13, 122, 70, 52, 22, 36] }, dst_opt1 = Ipv6RawExtHeader { next_header: 36, payload: [119, 8, 33, 223, 8, 7, 250, 227, 167, 201, 234, 147, 89, 51, 63, 121, 106, 141, 78, 166, 161, 243, 164, 246, 92, 41, 211, 145, 183, 192, 219, 145, 229, 225, 126, 223, 45, 249, 221, 56, 210, 163, 54, 113, 13, 155, 139, 220, 251, 74, 30, 178, 154, 153, 220, 206, 205, 222, 158, 198, 129, 42, 70, 102, 36, 165, 223, 48, 185, 82, 165, 24, 201, 1, 66, 38, 94, 109, 116, 172, 23, 28, 161, 123, 30, 81, 91, 47, 132, 129, 206, 10, 129, 130, 150, 56, 13, 65, 72, 213, 58, 58, 149, 29, 221, 170, 115, 2, 223, 30, 120, 6, 108, 99, 2, 13, 103, 235, 162, 31, 218, 168, 9, 238, 158, 29, 55, 49, 48, 239, 195, 214, 49, 132, 217, 139, 9, 100, 96, 244, 194, 2, 184, 227, 197, 13, 84, 198, 200, 1, 122, 4, 229, 106, 159, 195, 25, 217, 66, 69, 71, 51, 110, 245, 77, 33, 213, 134, 44, 183, 106, 56, 138, 86, 46, 88, 148, 211, 157, 249, 136, 66, 45, 115, 207, 250, 143, 196, 76, 28, 233, 13, 103, 63, 160, 126, 248, 49, 233, 86, 109, 47, 199, 11, 110, 115, 84, 215, 193, 6, 53, 39, 121, 37, 120, 242, 177, 150, 210, 201, 70, 93, 229, 61, 40, 175, 223, 171, 22, 58, 109, 79, 51, 82, 165, 216, 59, 140, 34, 178, 64, 54, 138, 202, 228, 66, 204, 154, 219, 47, 220, 212, 10, 239, 121, 69, 162, 223, 137, 0, 208, 82, 85, 254, 146, 223, 108, 36, 111, 95, 247, 41, 205, 251, 66, 86, 93, 217, 145, 151, 244, 42, 153, 218, 44, 203, 90, 125, 28, 28, 99, 187, 107, 214, 181, 172, 54, 203, 254, 129, 130, 71, 125, 234, 87, 237, 62, 132, 39, 101, 118, 129, 233, 106, 13, 27, 243, 229, 105, 81, 100, 53, 45, 136, 208, 99, 216, 105, 81, 97, 78, 219, 142, 71, 171, 176, 73, 54, 112, 54, 139, 202, 151, 80, 162, 233, 111, 239, 220, 2, 255, 102, 206, 22, 21, 166, 73, 178, 104, 218, 127, 207, 39, 186, 95, 203, 108, 29, 219, 237, 210, 102, 145, 211, 210, 100, 149, 82, 57, 115, 164, 119, 251, 88, 224, 127, 195, 191, 58, 90, 37, 3, 142, 230, 110, 172, 61, 223, 27, 88, 237, 249, 125, 200, 160, 74, 147, 164, 145, 92, 111, 123, 203, 219, 254, 15, 30, 74, 82, 3, 225, 192, 178, 227, 139, 27, 234, 170, 151, 69, 98, 113, 174, 4, 183, 214, 105, 137, 217, 130, 2, 110, 168, 31, 138, 44, 141, 55, 43, 188, 21, 231, 254, 201, 228, 177, 131, 67, 166, 55, 38, 104, 30, 13, 164, 1, 57, 70, 56, 34, 189, 126, 58, 125, 205, 45, 227, 10, 208, 142, 134, 108, 131, 160, 237, 50, 135, 226, 84, 218, 112, 141, 185, 20, 202, 230, 88, 254, 156, 166, 167, 161, 228, 181, 118, 185, 15, 157, 194, 114, 42, 161, 117, 182, 237, 245, 244, 81, 4, 31, 49, 76, 31, 14, 69, 160, 181, 2, 61, 107, 38, 86, 108, 252, 240, 213, 63, 98, 106, 16, 127, 66, 181, 246, 183, 26, 128, 238, 80, 215, 254, 119, 196, 215, 23, 199, 251, 153, 23, 181, 228, 54, 113, 115, 213, 91, 188, 112, 103, 193, 172, 76, 254, 163, 66, 33, 169, 110, 107, 136, 28, 245, 85, 42, 169, 13, 252, 167, 22, 67, 250, 24, 149, 46, 209, 185, 163, 52] }, route = Ipv6RawExtHeader { next_header: 97, payload: [147, 218, 145, 248, 152, 190, 47, 86, 204, 23, 38, 223, 229, 38, 236, 2, 243, 55, 64, 122, 55, 239, 177, 91, 16, 234, 183, 124, 211, 107, 74, 70, 155, 57, 151, 208, 84, 6, 41, 107, 109, 39, 5, 58, 110, 77, 90, 89, 72, 12, 35, 91, 162, 247, 101, 97, 103, 182, 42, 197, 242, 91, 170, 26, 154, 63, 23, 46, 178, 24, 107, 49, 31, 116, 129, 59, 71, 203, 236, 133, 27, 179, 97, 27, 45, 252, 222, 160, 23, 5, 163, 244, 138, 254, 239, 226, 197, 129, 151, 170, 42, 142, 73, 156, 79, 148, 114, 53, 244, 128, 185, 114, 232, 124, 2, 4, 106, 111, 131, 182, 63, 136, 109, 109, 182, 130, 133, 160, 208, 20, 153, 175, 165, 115, 119, 102, 146, 59, 230, 49, 67, 241, 159, 145, 86, 42, 175, 29, 197, 30, 200, 124, 209, 25, 196, 21, 111, 102, 177, 122, 222, 183, 236, 141, 207, 239, 80, 78, 78, 223, 189, 96, 87, 17, 109, 225, 54, 32, 157, 123, 222, 248, 173, 62, 19, 56, 238, 146, 88, 135, 33, 66, 236, 13, 41, 76, 210, 17, 167, 243, 20, 146, 238, 32, 98, 48, 46, 74, 240, 179, 67, 235, 48, 133, 97, 242, 211, 187, 248, 153, 150, 4, 181, 64, 16, 236, 197, 28, 64, 132, 248, 47, 123, 168, 132, 205, 186, 88, 99, 250, 79, 98, 81, 64, 28, 236, 19, 214, 212, 64, 143, 243, 221, 211, 244, 169, 95, 228, 220, 1, 133, 206, 195, 255, 153, 113, 21, 234, 15, 62, 178, 196, 117, 64, 100, 80, 223, 211, 40, 251, 70, 141, 207, 7, 130, 155, 42, 169, 74, 25, 170, 205, 127, 130, 250, 35, 78, 80, 103, 182, 123, 154, 148, 205, 172, 107, 243, 215, 154, 16, 28, 226, 128, 27, 227, 142, 244, 93, 1, 216, 238, 138, 88, 64, 66, 107, 34, 94, 99, 137, 47, 51, 15, 132, 85, 0, 206, 109, 55, 166, 32, 144, 93, 16, 234, 81, 86, 164, 219, 224, 216, 129, 102, 238, 113, 210, 135, 174, 25, 213, 51, 53, 54, 106, 41, 239, 115, 190, 62, 207, 143, 166, 148, 254, 192, 138, 41, 29, 171, 245, 102, 99, 23, 78, 45, 154, 167, 159, 200, 211, 50, 51, 183, 73, 66, 198, 31, 80, 232, 164, 90, 249, 92, 193, 189, 255, 27, 220, 213, 5, 63, 181, 196, 24, 67, 20, 223, 218, 115, 74, 120, 55, 204, 194, 59, 234, 92, 8, 227, 12, 151, 230, 107, 213, 253, 165, 166, 118, 16, 165, 28, 224, 72, 219, 5, 15, 90, 221, 111, 16, 244, 68, 94, 114, 193, 231, 175, 63, 39, 199, 199, 234, 222, 231, 226, 218, 1, 63, 183, 29, 82, 53, 74, 149, 107, 75, 170, 38, 34, 76, 118, 23, 192, 118, 53, 231, 119, 246, 95, 242, 148, 117, 250, 73, 205, 33, 91, 43, 17, 117, 69, 243, 95, 180, 46, 57, 140, 22, 247, 111, 191, 109, 207, 238, 205, 195, 172, 190, 221, 116, 151, 15, 84, 113, 12, 217, 100, 227, 54, 133, 100, 31, 107, 2, 10, 21, 252, 87, 177, 139, 162, 205, 30, 166, 35, 80, 192, 155, 65, 254, 64, 167, 1, 126, 141, 143, 90, 199, 3, 113, 220, 8, 55, 175, 198, 18, 105, 154, 100, 88, 246, 207, 55, 134, 18, 190, 232, 173, 97, 12, 112, 29, 119, 158, 83, 103, 44, 133, 190, 141, 123, 29, 175, 201, 197, 40, 181, 140, 142, 74, 201, 177, 88, 13, 125, 60, 109, 111, 193, 226, 176, 8, 85, 89, 242, 27, 207, 38, 57, 126, 34, 109, 76, 184, 50, 144, 9, 105, 212, 164, 177, 131, 9, 27, 137, 88, 48, 127, 173, 137, 146, 212, 108, 150, 139, 222, 213, 68, 123, 208, 199, 191, 192, 193, 206, 45, 166, 77, 7, 223, 21, 13, 166, 238, 117, 176, 254, 145, 208, 216, 127, 169, 158, 71, 177, 2, 163, 45, 51, 14, 180, 114, 255, 5, 48, 14] }, frag = Ipv6FragmentHeader { next_header: 18, fragment_offset: 4025, more_fragments: false, identification: 477210164 }, auth = IpAuthHeader { next_header: 185, spi: 1976768891, sequence_number: 35439408, raw_icv: [187, 120, 186, 102, 89, 209, 55, 48, 238, 60, 1, 222, 161, 207, 68, 76, 5, 87, 101, 1, 138, 105, 171, 252, 104, 137, 152, 219, 75, 111, 239, 180, 11, 163, 51, 254, 114, 193, 204, 101, 62, 183, 89, 122, 128, 217, 124, 206, 130, 230, 38, 224, 115, 38, 53, 57, 1, 80, 165, 118, 10, 176, 220, 109, 60, 208, 176, 238, 56, 81, 68, 88, 131, 154, 100, 180, 157, 78, 47, 3, 123, 3, 119, 205, 208, 76, 197, 191, 7, 207, 226, 199, 243, 18, 124, 75, 198, 220, 51, 248, 193, 135, 56, 227, 101, 8, 202, 189, 60, 203, 132, 66, 15, 222, 165, 30, 162, 51, 124, 136, 142, 44, 3, 2, 4, 253, 82, 57, 83, 101, 38, 40, 114, 19, 124, 114, 66, 249, 141, 72, 55, 185, 244, 170, 121, 131, 16, 224, 5, 239, 33, 38, 12, 211, 130, 255, 161, 50, 214, 121, 95, 173, 215, 155, 143, 232, 30, 29, 81, 32, 249, 155, 220, 210, 142, 90, 121, 40, 68, 250, 184, 208, 83, 134, 86, 143, 121, 3, 83, 214, 242, 75, 131, 10, 159, 33, 92, 169, 207, 152, 129, 104, 83, 47, 178, 49, 246, 167, 163, 60, 125, 229, 176, 76, 107, 224, 198, 211, 247, 150, 165, 30, 26, 119, 93, 104, 56, 186, 252, 113, 154, 109, 95, 117, 3, 111, 43, 102, 65, 74, 225, 86, 118, 22, 116, 136, 2, 216, 67, 13, 252, 54, 50, 103, 95, 59, 238, 233, 77, 144, 198, 17, 234, 34, 50, 173, 208, 116, 73, 254, 132, 160, 161, 47, 71, 74, 92, 124, 48, 207, 81, 154, 161, 32, 246, 245, 227, 212, 210, 85, 14, 149, 150, 30, 218, 98, 46, 229, 92, 57, 218, 166, 244, 62, 239, 0, 24, 169, 248, 214, 216, 34, 154, 26, 79, 217, 32, 208, 81, 248, 115, 109, 67, 115, 39, 15, 179, 193, 70, 176, 166, 219, 165, 246, 249, 207, 19, 38, 73, 66, 118, 170, 166, 163, 200, 59, 93, 222, 217, 27, 211, 82, 227, 193, 234, 223, 1, 246, 105, 147, 125, 172, 45, 19, 202, 186, 28, 204, 209, 23, 101, 31, 94, 228, 170, 244, 78, 194, 214, 134, 9, 184, 165, 173, 114, 220, 208, 155, 17, 172, 17, 245, 185, 89, 93, 8, 196, 49, 134, 178, 232, 205, 238, 108, 140, 188, 184, 138, 65, 166, 208, 230, 216, 67, 223, 193, 206, 254, 20, 204, 24, 40, 22, 193] }, dst_opt2 = Ipv6RawExtHeader { next_header: 107, payload: [134, 106, 38, 25, 109, 70, 100, 122, 225, 190, 31, 64, 177, 107, 80, 27, 146, 229, 239, 250, 74, 127, 12, 245, 135, 154, 30, 135, 161, 193, 240, 164, 100, 62, 45, 4, 50, 105, 203, 70, 159, 240, 53, 214, 148, 48, 73, 120, 48, 147, 47, 124, 241, 173, 75, 117, 75, 217, 79, 24, 95, 4, 163, 94, 237, 247, 87, 187, 217, 11, 97, 166, 68, 100, 55, 208, 51, 40, 251, 63, 219, 162, 51, 184, 12, 181, 217, 197, 221, 70, 148, 72, 255, 240, 179, 81, 57, 189, 190, 150, 255, 57, 106, 57, 59, 40, 198, 125, 55, 40, 130, 15, 183, 213, 100, 18, 77, 140, 214, 95, 175, 145, 69, 3, 186, 122, 218, 148, 187, 147, 211, 197, 96, 245] }, mobility = Ipv6RawExtHeader { next_header: 241, payload: [171, 79, 235, 150, 239, 209, 100, 229, 15, 148, 221, 219, 203, 233, 68, 96, 226, 180, 143, 134, 177, 119, 239, 155, 227, 103, 11, 186, 106, 193, 197, 5, 152, 248, 80, 111, 60, 69, 65, 57, 116, 168, 23, 236, 144, 247, 182, 6, 14, 121, 84, 21, 148, 51, 165, 51, 218, 1, 78, 116, 46, 169, 47, 31, 0, 219, 183, 98, 172, 143, 79, 126, 12, 175, 21, 45, 122, 178, 210, 231, 33, 74, 176, 172, 176, 221, 48, 16, 82, 90, 110, 86, 174, 22, 187, 55, 179, 185, 15, 0, 99, 222, 243, 110, 110, 146, 17, 232, 46, 40, 158, 9, 102, 222, 178, 241, 250, 53, 14, 18, 128, 235, 134, 173, 183, 71, 32, 248, 14, 218, 37, 159, 232, 196, 237, 227, 195, 240, 176, 114, 228, 48, 86, 246, 28, 68, 65, 92, 101, 210, 171, 15, 178, 79, 137, 138, 169, 159, 28, 78, 251, 84, 11, 72, 153, 122, 103, 239, 32, 125, 181, 177, 139, 213, 240, 245, 40, 176, 28, 61, 142, 61, 134, 248, 211, 13, 60, 147, 97, 161, 165, 171, 68, 253, 12, 53, 66, 95] }, hip = Ipv6RawExtHeader { next_header: 194, payload: [101, 166, 144, 235, 31, 112, 51, 164, 177, 179, 253, 44, 95, 118, 125, 154, 14, 17, 236, 67, 57, 200, 143, 160, 101, 202, 254, 138, 52, 168, 53, 235, 152, 217, 163, 4, 255, 26, 19, 92, 205, 234, 94, 251, 63, 125, 88, 50, 148, 39, 245, 25, 46, 57, 107, 105, 150, 16, 106, 1, 120, 19, 199, 85, 221, 230, 132, 17, 46, 32, 12, 34, 107, 102, 224, 80, 187, 226, 31, 215, 228, 151, 145, 193, 16, 250, 48, 20, 83, 116, 149, 32, 230, 3, 53, 180, 137, 233, 127, 96, 241, 73, 231, 128, 227, 222, 81, 104, 74, 128, 15, 156, 20, 99, 145, 92, 224, 0, 78, 12, 113, 104, 99, 167, 123, 222, 167, 67, 225, 182, 189, 221, 8, 221, 198, 175, 177, 67, 57, 84, 141, 104, 42, 165, 162, 110, 239, 238, 3, 208, 32, 87, 187, 61, 61, 176, 64, 43, 73, 189, 138, 42, 201, 204, 15, 46, 159, 186, 213, 79, 234, 204, 89, 54, 248, 188, 204, 123, 209, 239, 90, 239, 150, 19, 29, 112, 236, 68, 49, 35, 173, 100, 91, 243, 162, 3, 35, 22, 218, 33, 111, 118, 169, 213, 130, 80, 237, 60, 160, 38, 133, 236, 57, 40, 23, 122, 208, 202, 37, 108, 197, 167, 35, 184, 184, 54, 215, 250, 249, 6, 24, 222, 237, 123, 103, 33, 109, 23, 239, 224, 57, 154, 11, 38, 127, 98, 108, 38, 153, 140, 83, 118, 184, 141, 97, 103, 47, 173, 110, 142, 114, 179, 204, 69, 186, 182, 34, 82, 36, 102, 50, 61, 15, 172, 125, 60, 65, 198, 36, 1, 215, 150, 236, 241, 13, 92, 74, 50, 173, 167, 48, 115, 54, 197, 85, 53, 246, 169, 32, 53, 243, 241, 193, 108, 206, 0, 44, 146, 240, 179, 210, 33, 137, 88, 178, 181, 129, 68, 112, 21, 0, 45, 254, 214, 132, 148, 189, 182, 190, 178, 215, 253, 35, 5] }, shim6 = Ipv6RawExtHeader { next_header: 180, payload: [157, 124, 122, 172, 120, 24, 161, 42, 228, 16, 71, 241, 87, 116, 69, 195, 80, 106, 23, 34, 140, 81, 47, 19, 228, 209, 2, 243, 204, 172, 146, 184, 145, 96, 244, 90, 235, 249, 225, 203, 135, 124, 136, 35, 155, 246, 37, 92, 26, 32, 102, 181, 124, 236, 178, 1, 16, 226, 176, 49, 241, 251, 89, 144, 118, 126, 138, 42, 174, 211, 247, 115, 249, 22, 249, 148, 169, 148, 224, 211, 171, 248, 185, 102, 218, 103, 81, 141, 165, 213, 195, 103, 127, 249, 99, 245, 14, 117, 236, 159, 228, 59, 90, 171, 71, 79, 21, 88, 190, 188, 23, 85, 108, 166, 116, 83, 253, 196, 127, 160, 173, 46, 173, 4, 3, 158, 239, 16, 137, 171, 190, 149, 206, 50, 125, 244, 165, 0, 134, 208, 208, 144, 110, 239, 202, 69, 45, 8, 85, 78, 35, 241, 140, 237, 33, 41, 204, 216, 80, 206, 91, 198, 119, 127, 92, 29, 227, 232, 196, 189, 0, 81, 67, 229, 97, 235, 134, 142, 227, 18, 224, 95, 20, 60, 74, 193, 193, 55, 144, 119, 244, 159, 91, 133, 155, 0, 24, 8, 241, 210, 93, 115, 77, 85, 114, 117, 227, 129, 136, 56, 29, 93, 9, 231, 229, 156, 39, 18, 71, 12, 187, 207, 161, 224, 98, 29, 165, 184, 119, 240, 85, 240, 134, 119, 187, 171, 97, 71, 128, 213, 192, 179, 131, 151, 111, 122, 150, 213, 242, 38, 146, 101, 28, 238, 254, 228, 80, 179, 214, 80, 42, 70, 99, 165, 166, 157, 79, 108, 40, 98, 111, 204, 228, 44, 77, 54, 27, 167, 224, 37, 135, 159, 30, 49, 54, 61, 104, 36, 41, 228, 243, 206, 210, 113, 85, 126, 158, 215, 98, 205, 24, 46, 183, 12, 226, 203, 222, 169, 77, 31, 147, 180, 117, 93, 219, 115, 134, 100, 18, 8, 255, 75, 131, 208, 157, 116, 20, 195, 186, 63, 35, 192, 25, 173, 200, 216, 178, 191, 89, 56, 126, 221, 160, 70, 162, 128, 135, 52, 160, 242, 203, 81, 92, 128, 129, 45, 115, 18, 100, 121, 56, 178, 134, 147, 220, 175, 192, 173, 33, 106, 197, 80, 49, 43, 170, 128, 20, 254, 23, 58, 175, 200] }
diff --git a/proptest-regressions/internet/ipv6_header_slice.txt b/proptest-regressions/internet/ipv6_header_slice.txt
new file mode 100644
index 0000000..6872140
--- /dev/null
+++ b/proptest-regressions/internet/ipv6_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 10a03f9d86ad9fc6a192d9086246d29f800e3d0b526f3835a44babeeca3085dc # shrinks to header = Ipv6Header { traffic_class: 163, flow_label: 522586, payload_length: 32529, next_header: 18, hop_limit: 5, source: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 37, 225] }
diff --git a/proptest-regressions/internet/ipv6_raw_ext_header.txt b/proptest-regressions/internet/ipv6_raw_ext_header.txt
new file mode 100644
index 0000000..a70df53
--- /dev/null
+++ b/proptest-regressions/internet/ipv6_raw_ext_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 51a5e778e4c2530a23a9ad648f77743f4340334a030918acf88eaa3f0f69c26a # shrinks to header_a = Ipv6RawExtHeader { next_header: 8, payload: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 117, 104, 39, 161, 168, 179, 250, 76, 41, 74, 10, 221, 163, 103, 93, 115, 216, 31, 85, 118, 120, 140, 153, 57, 200, 142, 125, 155, 11, 75, 236, 214, 233, 175, 27, 220, 169, 119, 244, 23, 199, 225, 234, 252, 154, 87, 233, 63, 13, 219, 145, 221, 99, 148, 94, 178, 66, 141, 228, 129, 133, 128, 5, 60, 81, 161, 234, 247, 240, 40, 106, 156, 68, 215, 15, 105, 233, 48, 47, 138, 250, 27, 79, 122, 91, 0, 68, 216, 161, 180, 96, 191, 183, 124, 150, 196, 65, 79, 29, 63, 107, 207, 200, 71, 188, 210, 209, 106, 75, 130, 178, 230, 25, 17, 29, 49, 157, 176, 180, 89, 131, 171, 21, 230, 99, 8, 21, 177, 95, 75, 18, 249, 133, 117, 39, 251, 43, 206, 112, 12, 166, 11, 226, 89, 221, 112, 116, 9, 112, 58] }, header_b = Ipv6RawExtHeader { next_header: 39, payload: [37, 90, 196, 158, 230, 140, 245, 201, 210, 43, 224, 11, 63, 215, 60, 67, 70, 74, 116, 48, 141, 129, 95, 87, 212, 44, 14, 181, 159, 75, 224, 22, 120, 233, 62, 184, 105, 23, 233, 41, 209, 240, 72, 247, 217, 47, 7, 20, 124, 223, 69, 176, 145, 43, 207, 101, 139, 103, 106, 51, 29, 35, 5, 69, 176, 37, 27, 115, 202, 22, 27, 224, 52, 242, 10, 48, 90, 254, 122, 164, 92, 101, 246, 255, 170, 94, 21, 7, 138, 2, 211, 127, 172, 17, 121, 106, 4, 52, 163, 53, 63, 217, 209, 185, 34, 26, 4, 107, 250, 237, 94, 235, 251, 17, 61, 124, 5, 157, 136, 106, 1, 253, 188, 218, 68, 244, 132, 158, 74, 250, 120, 171, 198, 83, 203, 91, 238, 147, 156, 224, 87, 228, 227, 149, 210, 172, 80, 53, 253, 227, 201, 191, 1, 51, 129, 63, 236, 18, 229, 230, 100, 123, 30, 54, 45, 204, 67, 209, 34, 11, 252, 88, 250, 45, 167, 65, 20, 101, 174, 69, 155, 97, 139, 237, 192, 214, 79, 5, 220, 88, 235, 48, 135, 95, 205, 176, 187, 241, 56, 70, 223, 209, 123, 94, 59, 85, 225, 130, 74, 66, 96, 220, 99, 0, 253, 249, 12, 250, 200, 116, 207, 201, 40, 254, 40, 142, 186, 215, 49, 162, 154, 195, 236, 197, 90, 72, 58, 4, 194, 73, 8, 92, 156, 166, 209, 16, 119, 81, 148, 94, 202, 199, 230, 183, 133, 190, 217, 249, 77, 169, 226, 36, 81, 194, 48, 146, 28, 82, 71, 222, 214, 121, 177, 249, 114, 93, 101, 167, 136, 88, 126, 34, 104, 156, 1, 207, 118, 34, 8, 80, 177, 190, 194, 41, 77, 240, 212, 76, 190, 190, 131, 181, 50, 109, 197, 49, 114, 32, 190, 46, 202, 74, 253, 118, 188, 143, 217, 166, 239, 24, 15, 57, 195, 242, 37, 238, 184, 161, 215, 146, 92, 197, 47, 18, 78, 34, 141, 103, 147, 141, 155, 127, 27, 146, 194, 231, 244, 192, 41, 253, 88, 38, 178, 195, 107, 199, 127, 208, 153, 108, 165, 1, 19, 45, 136, 105, 201, 74, 91, 140, 65, 17, 212, 197, 88, 35, 10, 64, 100, 197, 46, 154, 248, 136, 82, 14, 26, 115, 230, 117, 51, 36, 250, 65, 67, 149, 188, 246, 196, 237, 109, 63, 130, 6, 45, 35, 251, 246, 78, 154, 212, 149, 215, 215, 222, 14, 105, 96, 1, 172, 183, 84, 253, 242, 103, 45, 84, 179, 161, 204, 230, 13, 49, 143, 48, 51, 97, 159, 118, 180, 215, 98, 37, 59, 243, 81, 222, 209, 196, 124, 79, 127, 246, 26, 110, 191, 219, 144, 159, 95, 214, 41, 139, 137, 19, 26, 44, 228, 143, 111, 10, 125, 32, 60, 248, 45, 115, 30, 59, 90, 252, 32, 13, 124, 54, 144, 240, 144, 171, 239, 207, 157, 64, 88, 232, 224, 83, 108, 245, 58, 196, 15, 94, 73, 227, 51, 113, 153, 243, 126, 227, 210, 48, 146, 106, 173, 166, 44, 3, 158, 57, 56, 7, 80, 87, 195, 205, 184, 141, 34, 147, 100, 79, 172, 51, 77, 247, 116, 118, 227, 43, 75, 234, 239, 70, 55, 20, 190, 130, 155, 137, 214, 36, 183, 174, 44, 3, 31, 190, 249, 255, 119, 123, 231, 2, 56, 22, 190, 126, 74, 36, 91, 28, 24, 132, 5, 19, 247, 209, 132, 225, 239, 253, 96, 147, 88, 173, 57, 201, 235, 93, 86, 216, 92, 169, 35, 167, 84, 210, 71, 37, 113, 176, 4, 32, 172, 116, 138, 253, 172, 163, 112, 126, 106, 52, 243, 161, 15, 211, 12, 220, 66, 101, 166, 215, 74, 112, 177, 169, 167, 159, 150, 208, 177, 33, 188, 126, 130, 219, 104, 77, 73, 2, 223, 219, 4, 255, 65, 219, 251, 164, 145, 173, 72, 238, 43, 77, 32, 52, 143, 1, 229, 198, 19, 220, 120, 151, 32, 93, 145, 255, 156, 218, 122, 174, 155, 77, 236, 119, 242, 88, 114, 128, 1, 109, 90, 72, 87, 36, 250, 36, 253, 211, 228, 182, 49, 48, 24, 36, 158, 69, 6, 58, 192, 97, 19, 93, 154, 118, 71, 161, 147, 99, 128, 232, 164, 103, 184, 53, 248, 166, 185, 125, 163, 182, 92, 29, 5, 169, 87, 46, 50, 28, 11, 142, 207, 51, 175, 15, 210, 25, 26, 175, 37, 78, 6, 251, 180, 239, 91, 205, 41, 185, 0, 155, 101, 93, 228, 213, 21, 190, 152, 239, 219, 70, 36, 78, 102, 93, 38, 1, 61, 89, 77, 198, 136, 157, 78, 254, 217, 143, 197, 13, 76, 69, 111, 152, 194, 105, 28, 22, 2, 178, 195, 104, 166, 218, 234, 244, 122, 31, 17, 250, 67, 56, 109, 167, 237, 64, 167, 191, 165, 116, 239, 53, 242, 149, 2, 118, 91, 24, 195, 239, 1, 75, 99, 154, 254, 118, 218, 180, 48, 104, 42, 150, 94, 90, 79, 75, 111, 237, 102, 166, 129, 41, 225, 12, 99, 89, 137, 123, 40, 66, 184, 219, 208, 127, 8, 98, 27, 61, 195, 58, 56, 197, 39, 232, 241, 4, 148, 68, 117, 210, 90, 121, 75, 12, 191, 1, 161, 254, 136, 24, 138, 19, 203, 94, 117, 24, 181, 176, 38, 51, 207, 18, 124, 251, 29, 168, 144, 28, 238, 175, 249, 165, 2, 128, 127, 114, 194, 42, 70, 232, 182, 32, 171, 245, 49, 238, 16, 203, 108, 163, 235, 136, 169, 199, 254, 59, 48, 195, 192, 136, 244] }
+cc 70db8a0a9053abecfe40d394fda445b7e30ed68ff3f1cbcaae6b28251c0545c8 # shrinks to header = Ipv6RawExtHeader { next_header: 42, payload: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 83, 230, 184, 76, 226, 242, 9, 81, 26, 154, 175, 74, 114, 68, 121, 157, 188, 191, 28, 135, 119, 15, 88, 206, 194, 217, 240, 247, 56, 249, 217, 209, 133, 201, 190, 71, 105, 64, 220, 12, 185, 59, 143, 90, 208, 86, 61, 239, 142, 29, 151, 152, 28, 136, 104, 246, 133, 94, 220, 33, 188, 69, 125, 120, 38, 29, 62, 159, 84, 178, 9, 105, 21, 125, 94, 93, 27, 52, 135, 160, 157, 30, 215, 71, 19, 61, 206, 150, 154, 215, 215, 80, 233, 118, 196, 131, 72, 7, 90, 124, 86, 110, 43, 250, 221, 102, 117, 2, 225, 160, 125, 217, 227, 180, 60, 170, 174, 20, 15, 216, 143, 235, 74, 243, 46, 141, 186, 246, 205, 208, 148, 68, 29, 180, 54, 248, 111, 202, 12, 217, 127, 64, 13, 182, 83, 255, 44, 153, 244, 200, 226, 232, 1, 68, 35, 63, 41, 71, 200, 187, 143, 92, 20, 205, 146, 241, 225, 210, 108, 229, 247, 165, 30, 168, 1, 56, 206, 104, 126, 224, 177, 154, 144, 164, 96, 153, 87, 205, 160, 225, 81, 202, 1, 49, 100, 222, 173, 66, 151, 114, 129, 141, 39, 131, 49, 70, 89, 44, 10, 226, 190, 1, 116, 229, 57, 245, 130, 50, 97, 76, 254, 219, 67, 49, 184, 70, 38, 132, 200, 234, 226, 253, 124, 31, 39, 119, 53, 74, 19, 97, 171, 136, 55, 96, 9, 255, 254, 35, 82, 112, 85, 44, 119, 3, 56, 8, 107, 244, 57, 250, 79, 14, 185, 125, 101, 96, 188, 43, 29, 205, 100, 92, 27, 23, 34, 195, 87, 96, 129, 75, 55, 77, 75, 168, 158, 155, 255, 225, 6, 201, 35, 201, 49, 125, 126, 153, 189, 32, 29, 179, 77, 236, 37, 18, 22, 9, 166, 151, 193, 154, 242, 2, 226, 158, 22, 160, 36, 130, 104, 81, 29, 10, 185, 147, 91, 184, 121, 236, 222, 89, 197, 15, 210, 120, 30, 253, 231, 216, 172, 92, 170, 119, 214, 130, 143, 134, 177, 27, 37, 72, 159, 205, 213, 105, 119, 36, 93, 100, 125, 90, 122, 204, 39, 246, 123, 78, 222, 150, 197, 246, 249, 248, 135, 126, 180, 83, 21, 121, 116, 205, 188, 177, 210, 163, 66, 90, 90, 130, 152, 74, 57, 118, 35, 225, 21, 186, 245, 241, 22, 205, 230, 163, 104, 114, 111, 119, 89, 143, 121, 102, 96, 72, 162, 33, 108, 156, 216, 1, 241, 38, 7, 111, 84, 126, 249, 205, 35, 251, 92, 175, 86, 252, 216, 222, 66, 132, 138, 182, 222, 96, 86, 161, 49, 43, 233, 110, 141, 221, 30, 241, 91, 163, 205, 21, 229, 184, 28, 160, 213, 190, 61, 13, 230, 178, 48, 225, 51, 210, 45, 206, 23, 151, 61, 240, 158, 74, 179, 93, 237, 62, 99, 223, 78, 51, 53, 31, 21, 129, 132, 192, 137, 0, 58, 93, 198, 175, 32, 19, 137, 140, 45, 188, 85, 153, 130, 11, 190, 11, 244, 77, 0, 104, 233, 148, 229, 105, 46, 76, 72, 218, 218, 54, 212, 69, 230, 77, 255, 222, 43, 27, 23, 217, 185, 66, 216, 104, 145, 41, 146, 188, 112, 88, 217, 142, 124, 96, 95, 57, 178, 44, 207, 129, 14, 16, 59, 238, 172, 233, 63, 182, 177, 221, 109, 102, 149, 250, 239, 230, 155, 238, 26, 165, 205, 183, 107, 106, 125, 115, 75, 188, 205, 49, 246, 42, 168, 221, 200, 39, 129, 1, 116, 172, 111, 46, 173, 4, 249, 246, 134, 155, 89, 37, 86, 50, 12, 241, 160, 135, 192, 35, 99, 79, 127, 147, 157, 185, 70, 76, 64, 238, 200, 221, 113, 117, 17, 149, 7, 89, 38, 247, 51, 133, 179, 112, 52, 127, 46, 63, 106, 50, 80, 250, 247, 183, 17, 129, 199, 0, 160, 198, 100, 31, 204, 199, 113, 159, 197, 144, 11, 44, 23, 223, 224, 241, 246, 7, 16, 53, 1, 158, 30, 54, 7, 36, 217, 23, 36, 79, 143, 171, 222, 11, 202, 3, 224, 69, 105, 90, 201, 27, 46, 186, 94, 102, 171, 150, 186, 54, 137, 16, 221, 169, 140, 117, 31, 10, 120, 92, 210, 111, 57, 194, 128, 152, 139, 230, 153, 43, 2, 128, 15, 212, 104, 254, 112, 141, 131, 228, 22, 120, 40, 1, 108, 117, 221, 209, 144, 136, 62, 30, 73, 252, 188, 181, 192, 74, 30, 76, 191, 251, 252, 97, 66, 226, 136, 123, 85, 202, 251, 159, 101, 186, 21, 129, 221, 200, 49, 12, 51, 165, 104, 175, 213, 243, 102, 97, 110, 200, 6, 207, 76, 177, 165, 49, 196, 94, 212, 203, 68, 132, 162, 144, 3, 217, 221, 29, 88, 98, 44, 83, 161, 55, 255, 24, 193, 132, 213, 55, 37, 229, 25, 250, 61, 182, 227, 17, 220, 210, 145, 133, 57, 2, 175, 28, 136, 205, 95, 130, 123, 161, 119, 77, 163, 27, 18, 203, 84, 129, 133, 108, 25, 107, 241, 100, 210, 169, 193, 245, 36, 29, 89, 55, 14, 128, 180, 1, 197, 21, 12, 4, 45, 207, 241, 188, 88, 183, 172, 13, 18, 206, 5, 198, 127, 65, 44, 231, 160, 179, 193, 70, 28, 15, 72, 87, 150, 38, 122, 69, 152, 17, 16, 59, 11, 81, 89, 194, 160, 18, 191, 33, 32, 182, 177, 59, 52, 23, 70, 205, 240, 217, 241, 29, 106, 158, 4, 126, 193, 20, 159, 38, 54, 17, 162, 175, 159, 247, 89, 234, 163, 255, 220, 87, 198, 67, 158, 81, 36, 66, 110, 196, 30, 214, 2, 129, 180, 134, 61, 139, 43, 201, 11, 252, 67, 186, 34, 212, 195, 58, 175, 225, 176, 229, 151, 53, 112, 138, 124, 214, 183, 168, 226, 226, 172, 183, 178, 121, 87, 190, 114, 32, 145, 27, 152, 1, 234, 90, 5, 17, 224, 44, 74, 153, 215, 214, 118, 98, 50, 130, 174, 12, 124, 252, 194, 66, 48, 59, 213, 121, 124, 106, 6, 152, 139, 170, 2, 172, 85, 47, 145, 24, 14, 122, 194, 22, 118, 104, 231, 145, 30, 122, 191, 195, 108, 46, 171, 172, 192, 71, 9, 171, 102, 43, 90, 188, 74, 150, 8, 16, 213, 138, 63, 138, 198, 16, 50, 12, 235, 15, 210, 91, 193, 180, 227, 167, 104, 110, 73, 54, 45, 77, 75, 128, 116, 58, 87, 182, 178, 103, 105, 219, 232, 78, 199, 92, 35, 132, 63, 236, 141, 90, 132, 117, 205, 220, 39, 37, 135, 85, 200, 237, 137, 6, 181, 94, 90, 177, 181, 205, 195, 41, 114, 226, 94, 157, 110, 142, 106, 128, 61, 44, 90, 98, 92, 180, 79, 158, 174, 255, 32, 15, 27, 121, 177, 118, 154, 9, 221, 130, 84, 157, 111, 146, 11, 248, 121, 159, 57, 127, 90, 181, 213, 209, 164, 57, 149, 195, 1, 117, 44, 209, 71, 120, 243, 160, 33, 251, 77, 4, 192, 105, 169, 124, 85, 66, 66, 254, 175, 45, 173, 236, 1, 97, 42, 133, 102, 133, 31, 169, 91, 41, 93, 174, 1, 235, 239, 102, 193, 88, 66, 51, 17, 103, 147, 105, 111, 181, 143, 98, 176, 100, 43, 73, 14, 18, 21, 79, 64, 164, 163, 15, 25, 61, 30, 44, 119, 140, 218, 137, 192, 110, 206, 78, 122, 120, 120, 204, 198, 86, 240, 40, 88, 69, 232, 72, 101, 142, 65, 69, 36, 20, 52, 16, 61, 63, 198, 10, 69, 152, 37, 9, 163, 244, 229, 81, 117, 141, 32, 220, 89, 17, 135, 162, 144, 45, 3, 250, 138, 82, 191, 14, 77, 156, 53, 14, 96, 64, 120, 180, 6, 85, 6, 131, 203, 62, 51, 127, 62, 214, 167, 168, 98, 12, 203, 247, 104, 13, 253, 113, 219, 27, 9, 137, 72, 64, 42, 18, 180, 224, 30, 107, 156, 26, 35, 25, 237, 206, 218, 181, 190, 64, 78, 102, 13, 77, 174, 233, 2, 68, 61, 17, 192, 112, 216, 249, 160, 54, 176, 207, 164, 37, 241, 60, 96, 199, 115, 161, 59, 138, 70, 118, 49, 130, 233, 16, 181, 132, 27, 58, 30, 25, 113, 253, 1, 141, 234, 74, 164, 83, 130, 15, 127, 135, 15, 198, 41, 3, 33, 185, 225, 56, 131, 210, 137, 173, 209, 24, 161, 22, 42, 212, 87, 138, 33, 16, 100, 62, 208, 203, 6, 146, 131, 101, 91, 254, 135, 13, 24, 118, 134, 206, 166, 239, 184, 105, 206, 245, 101, 154, 148, 251, 55, 136, 22, 232, 201, 4, 48, 209, 94, 126] }
diff --git a/proptest-regressions/internet/ipv6_slice.txt b/proptest-regressions/internet/ipv6_slice.txt
new file mode 100644
index 0000000..3c95cab
--- /dev/null
+++ b/proptest-regressions/internet/ipv6_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 0351daa6a50705db06d9b8e30a2862462cb85849b1256f7cf12b60dc7a268747 # shrinks to ipv6_base = Ipv6Header { traffic_class: 0, flow_label: 0, payload_length: 0, next_header: 0, hop_limit: 0, source: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }, auth = IpAuthHeader { next_header: 51, spi: 0, sequence_number: 0, raw_icv: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 223, 109, 73, 187, 107, 151, 83, 131, 9, 151, 32, 68, 157, 155, 64, 139, 24, 229, 141, 116, 248, 90, 43, 74, 28, 35, 30, 240, 175, 169, 149, 215, 211, 193, 69, 206, 104, 141, 198, 97, 126, 251, 147, 204, 6, 19, 0, 48, 62, 21, 44, 231, 179, 59, 208, 219, 3, 219, 84, 88, 96, 235, 42, 92, 49, 137, 26, 14, 141, 101, 231, 130, 184, 10, 179, 232, 74, 121, 255, 244, 129, 201, 64, 150, 79, 146, 120, 83, 74, 136, 156, 207, 99, 39, 44, 94, 150, 219, 39, 162, 211, 203, 184, 115, 99, 243, 132, 202, 169, 145, 218, 69, 191, 130, 158, 26, 15, 151, 27, 47, 242, 231, 216, 72, 171, 101, 110, 73, 73, 86, 69, 128, 102, 29, 133, 205, 32, 10, 254, 7, 191, 181, 215, 164, 20, 62, 48, 235, 158, 96, 45, 35, 73, 84, 194, 30, 104, 127, 131, 203, 61, 28, 54, 50, 78, 1, 15, 95, 76, 148, 213, 71, 209, 209, 251, 172, 219, 225, 161, 92, 136, 106, 54, 245, 213, 246, 118, 136, 244, 121, 106, 125, 127, 73, 135, 33, 29, 49, 18, 112, 22, 225, 94, 18, 185, 180, 113, 139, 58, 143, 17, 200, 200, 79, 92, 75, 212, 248, 42, 101, 243, 111, 27, 187, 47, 243, 3, 42, 97, 0, 130, 91, 99, 213, 234, 165, 215, 135, 171, 168, 56, 229, 102, 170, 124, 92, 78, 67, 239, 41, 84, 173, 201, 79, 141, 71, 144, 104, 39, 180, 164, 35, 37, 142, 41, 132, 68, 193, 184, 197, 39, 201, 156, 98, 55, 222, 102, 13, 74, 32, 124, 33, 241, 246, 76, 183, 118, 225, 255, 210, 47, 134, 179, 110, 210, 96, 201, 137, 234, 232, 86, 85, 204, 18, 55, 158, 205, 221, 109, 120, 123, 195, 24, 219, 79, 103, 158, 231, 95, 55, 66, 144, 255, 1, 196, 1, 113, 31, 223, 249, 162, 8, 122, 106, 183, 225, 214, 173, 16, 61, 83, 159, 63, 39, 193, 189, 250, 125, 5, 106, 91, 16, 28, 111, 105, 164, 19, 183, 41, 30, 194, 194, 1, 118, 162, 147, 74, 141, 102, 210, 245, 191, 186, 255, 29, 68, 149, 98, 23, 174, 17, 194, 64, 30, 205, 222, 131, 39, 55, 242, 72, 191, 117, 204, 175, 23, 255, 182, 218, 43, 139, 33, 164, 85, 132, 34, 32, 236, 189, 168, 181, 146, 226, 123, 253, 109, 127, 27, 57, 237, 84, 213, 202, 86, 241, 190, 239, 155, 175, 107, 216, 67, 111, 14, 100, 238, 62, 134, 248, 241, 165, 215, 29, 83, 159, 91, 26, 141, 117, 71, 170, 36, 142, 143, 74, 41, 223, 117, 198, 128, 142, 226, 206, 213, 38, 224, 189, 239, 51, 251, 211, 159, 245, 248, 251, 21, 158, 13, 170, 211, 128, 90, 124, 34, 9, 24, 206, 178, 197, 90, 34, 120, 254, 10, 21, 155, 227, 220, 155, 63, 193, 135, 76, 77, 87, 14, 182, 165, 193, 90, 236, 86, 189, 195, 195, 214, 148, 164, 179, 54, 103, 229, 126, 219, 69, 13, 174, 64, 221, 189, 62, 215, 174, 2, 137, 199, 50, 152, 38, 144, 78, 180, 154, 40, 27, 190, 47, 96, 222, 103, 70, 6, 35, 140, 215, 47, 140, 235, 66, 47, 108, 236, 48, 80, 115, 11, 246, 67, 62, 157, 197, 49, 188, 175, 175, 207, 155, 99, 96, 55, 114, 81, 63, 192, 139, 139, 252, 130, 82, 117, 69, 162, 120, 34, 130, 158, 255, 159, 130, 46, 59, 59, 18, 137, 65, 188, 146, 230, 23, 172, 120, 125, 47, 81, 156, 160, 253, 255, 45, 62, 208, 182, 68, 141, 143, 88, 34, 248, 124, 110, 45, 201, 54, 153, 161, 188, 170, 93, 56, 131, 224, 91, 14, 9, 247, 186, 176, 231, 139, 27, 106, 96, 95, 4, 33, 113, 104, 254, 11, 18, 237, 37, 74, 231, 138, 203, 200, 119, 241, 38, 63, 75, 198, 105, 3, 182, 149, 4, 76, 192, 11, 201, 79, 222, 157, 115, 104, 142, 190, 17, 46, 42, 231, 203, 12, 65, 232, 196, 242, 61, 169, 34, 162, 176, 223, 17, 157, 222, 40, 16, 221, 45, 223, 150, 202, 117, 190, 176, 200, 111, 59, 247, 95, 228, 229, 44, 221, 150, 244, 133, 121, 228, 132, 30, 231, 144, 112, 47, 240, 47, 211, 184, 134, 1, 159, 242, 214, 93, 137, 47, 177, 160, 70, 185, 72, 94, 58, 199, 105, 126, 242, 233, 73, 29, 151, 30, 50, 21, 104, 149, 28, 147, 108, 199, 131, 121, 234, 208, 90, 94, 44, 48, 241, 170, 89, 119, 90, 195, 99, 56, 132, 46, 204, 141, 227, 13, 93, 169, 227, 160, 29, 149, 97, 71, 37, 39, 209, 175, 122, 151, 81, 62, 59, 210, 91, 137, 132, 163, 196, 64, 26, 170, 209, 42, 136, 173, 10, 144, 255, 185, 228, 51, 200, 25, 45, 152, 206, 189, 200, 250, 186, 225, 180, 119, 148, 80, 68, 40, 130, 150, 2, 78, 120, 124, 168, 247, 80, 192, 37, 14, 214, 142, 149, 83, 198, 84, 202, 97, 121, 253, 102, 193, 151, 128, 184, 89, 138, 119, 77, 119, 94, 255, 120, 35, 16, 215, 23, 230, 28] }
diff --git a/proptest-regressions/internet/lax_ip_slice.txt b/proptest-regressions/internet/lax_ip_slice.txt
new file mode 100644
index 0000000..fd7cb0c
--- /dev/null
+++ b/proptest-regressions/internet/lax_ip_slice.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 1fbfe7264563272f86ba3b12ea70cb75c8053c0ca58de35c6de9a1d294473e7b # shrinks to ipv4_header = Ipv4Header { dscp: Ipv4Dscp(0), ecn: Ipv4Ecn(0), total_len: 18538, identification: 0, dont_fragment: false, more_fragments: false, fragment_offset: IpFragOffset(6350), time_to_live: 0, protocol: 2 (IGMP - Internet Group Management), header_checksum: 16491, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [] }, ipv4_exts = Ipv4Extensions { auth: Some(IpAuthHeader { next_header: 17 (UDP - User Datagram), spi: 3883027753, sequence_number: 2737770242, raw_icv: [66, 182, 216, 215, 50, 209, 106, 93, 188, 185, 64, 107, 50, 111, 71, 46, 149, 253, 240, 159, 230, 109, 201, 96, 79, 208, 199, 225, 64, 182, 63, 18, 110, 90, 51, 74, 28, 121, 65, 68, 61, 213, 152, 169, 105, 159, 225, 107, 69, 227, 252, 164, 190, 116, 170, 115, 191, 38, 90, 224, 76, 101, 59, 18, 53, 177, 153, 192, 187, 236, 68, 184, 22, 194, 149, 157, 135, 53, 50, 18, 59, 34, 67, 231, 238, 7, 0, 241, 1, 242, 167, 0, 136, 71, 58, 239, 226, 16, 236, 51, 240, 40, 144, 245, 191, 23, 204, 101, 104, 187, 63, 236, 196, 200, 13, 4, 111, 165, 86, 69, 109, 146, 131, 84, 14, 0, 10, 247, 40, 117, 230, 4, 75, 108, 125, 201, 251, 38, 166, 35, 185, 48, 215, 142, 191, 114, 63, 162, 112, 84, 81, 94, 169, 144, 19, 99, 3, 243, 3, 125, 192, 189, 82, 57, 99, 24, 51, 61, 171, 236, 0, 203, 170, 234, 61, 5, 86, 62, 183, 241, 93, 99, 208, 107, 247, 149, 35, 31, 39, 72, 57, 250, 187, 114, 25, 17, 193, 202, 204, 218, 172, 242, 201, 77, 133, 49, 8, 111, 186, 243, 11, 209, 113, 74, 117, 126, 217, 179, 60, 48, 74, 11, 100, 118, 42, 14, 111, 78, 105, 93, 69, 229, 14, 151, 114, 96, 133, 60, 216, 100, 248, 150, 203, 154, 168, 170, 230, 8, 8, 98, 163, 248, 149, 160, 141, 139, 63, 251, 202, 226, 28, 173, 144, 16, 2, 29, 155, 128, 125, 162, 178, 16, 218, 45, 222, 72, 155, 21, 58, 7, 218, 249, 61, 53, 91, 236, 221, 80, 97, 225, 253, 122, 6, 233, 211, 183, 44, 57, 126, 106, 134, 42, 152, 233, 18, 1, 241, 228, 241, 33, 113, 192, 234, 61, 182, 189, 24, 74, 167, 3, 82, 32, 226, 19, 29, 169, 87, 221, 105, 243, 82, 132, 3, 53, 190, 115, 234, 249, 54, 11, 13, 50, 177, 89, 171, 4, 141, 226, 8, 92, 65, 227, 142, 17, 129, 154, 47, 40, 181, 112, 139, 12, 230, 12, 56, 194, 111, 11, 104, 61, 178, 101, 128, 126, 189, 121, 254, 89, 79, 151, 122, 178, 43, 55, 108, 197, 88, 229, 176, 123, 32, 221, 145, 114, 58, 191, 132, 147, 164, 110, 191, 26, 3, 140, 193, 147, 121, 68, 101, 139, 18, 0, 35, 237, 153, 32, 228, 134, 238, 196, 167, 48, 187, 73, 187, 136, 59, 25, 67, 47, 192, 114, 119, 234, 14, 76, 98, 109, 45, 112, 135, 181, 69, 103, 188, 184, 137, 105, 142, 33, 255, 174, 96, 75, 34, 147, 9, 91, 109, 213, 70, 8, 20, 218, 217, 249, 25, 121, 124, 237, 138, 161, 73, 138, 248, 23, 188, 226, 204, 167, 120, 135, 94, 38, 191, 105, 37, 64, 176, 126, 107, 87, 225, 67, 254, 108, 161, 246, 122, 189, 199, 150, 36, 138, 139, 211, 227, 204, 53, 233, 89, 223] }) }, ipv6_header = Ipv6Header { traffic_class: 154, flow_label: Ipv6FlowLabel(861270), payload_length: 56480, next_header: 114 (any 0-hop protocol), hop_limit: 22, source: [220, 109, 20, 44, 182, 208, 142, 209, 157, 78, 179, 231, 195, 135, 19, 28], destination: [73, 191, 150, 175, 186, 61, 182, 142, 189, 1, 71, 193, 91, 26, 174, 15] }, mut ipv6_exts = Ipv6Extensions { hop_by_hop_options: None, destination_options: None, routing: None, fragment: None, auth: Some(IpAuthHeader { next_header: 17 (UDP - User Datagram), spi: 2021021679, sequence_number: 165850750, raw_icv: [168, 212, 254, 154, 236, 93, 243, 36, 217, 177, 145, 13, 21, 62, 45, 216, 1, 220, 178, 13, 80, 237, 143, 196, 107, 115, 107, 216, 116, 209, 227, 217, 42, 13, 85, 241, 56, 143, 134, 63, 219, 45, 163, 158, 238, 35, 51, 64, 236, 184, 37, 161, 231, 144, 221, 82, 128, 247, 124, 167, 35, 140, 225, 125, 59, 20, 254, 189, 55, 160, 62, 25, 103, 40, 135, 108, 204, 159, 220, 214, 215, 13, 134, 197, 137, 119, 172, 152, 211, 125, 154, 144, 97, 181, 0, 135, 42, 253, 123, 171, 1, 254, 65, 219, 182, 98, 38, 79, 34, 147, 93, 59, 70, 136, 245, 40, 90, 149, 255, 242, 22, 170, 253, 98, 30, 215, 246, 249, 136, 129, 198, 30, 89, 7, 233, 74, 255, 84, 36, 43, 49, 117, 62, 51, 210, 94, 204, 212, 153, 235, 103, 38, 246, 102, 50, 110, 208, 87, 105, 151, 80, 154, 214, 63, 159, 116, 207, 102, 194, 145, 104, 127, 180, 62, 190, 124, 242, 197, 54, 26, 188, 41, 127, 14, 238, 45, 56, 44, 217, 244, 33, 123, 109, 148, 17, 220, 138, 251, 129, 75, 59, 29, 230, 87, 25, 81, 233, 228, 74, 218, 166, 107, 14, 96, 157, 130, 171, 29, 28, 152, 18, 248, 3, 137, 76, 115, 19, 194, 142, 195, 151, 215, 197, 190, 16, 185, 200, 71, 220, 1, 16, 181, 30, 149, 248, 28, 78, 249, 71, 116, 42, 52, 124, 244, 34, 96, 87, 206, 181, 252, 113, 27, 217, 11, 237, 35, 220, 228, 198, 93, 22, 172, 38, 217, 218, 111, 46, 166, 188, 183, 161, 208, 6, 100, 136, 41, 101, 68, 72, 80, 207, 47, 62, 253, 131, 221, 178, 5, 174, 172, 120, 236, 99, 252, 198, 177, 7, 137, 84, 242, 254, 237, 20, 118, 106, 155, 185, 48, 65, 112, 239, 76, 43, 40, 33, 185, 75, 94, 170, 157, 42, 7, 72, 29, 57, 194, 228, 92, 195, 116, 230, 235, 17, 248, 85, 181, 98, 99, 134, 232, 112, 141, 4, 77, 251, 43, 121, 102, 126, 2, 253, 109, 82, 89, 8, 161, 78, 96, 243, 22, 129, 53, 213, 188, 81, 4, 252, 38, 99, 175, 127, 54, 143, 6, 156, 103, 126, 128, 19, 129, 57, 199, 34, 96, 10, 191, 178, 243, 180, 185, 75, 108, 105, 182, 99, 163, 138, 153, 1, 214, 220, 145, 119, 69, 242, 102, 210, 21, 93, 215, 26, 151, 13, 30, 68, 147, 72, 172, 12, 129, 183, 35, 122, 166, 9, 245, 44, 162, 99, 213, 89, 245, 223, 169, 20, 29, 105, 225, 228, 76, 48, 41, 212, 53, 51, 180, 30, 70, 3, 255, 200, 173, 237, 217, 4, 139, 50, 188, 53, 125, 204, 152, 74, 233, 9, 37, 34, 80, 4, 115, 59, 89, 155, 165, 188, 42, 28, 215, 162, 99, 16, 207, 25, 110, 52, 46, 174, 221, 124, 99, 165, 56, 69, 147, 49, 94, 34, 46, 87, 107, 193, 43, 96, 241, 187, 173, 203, 128, 41, 25, 61, 229, 27, 65, 94, 13, 90, 157, 245, 212, 60, 14, 149, 227, 89, 58, 191, 76, 19, 125, 181, 57, 83, 235, 107, 139, 65, 121, 170, 178, 174, 89, 255, 91, 225, 142, 238, 229, 143, 158, 165, 162, 11, 17, 30, 250, 146, 248, 115, 253, 245, 231, 162, 227, 129, 251, 110, 52, 226, 1, 181, 49, 56, 246, 22, 105, 175, 60, 9, 201, 29, 217, 183, 13, 19, 42, 131, 113, 41, 245, 168, 244, 48, 246, 73, 130, 245, 191, 251, 218, 66, 216, 241, 87, 96, 215, 106, 184, 51, 215, 57, 123, 203, 222, 212, 239, 103, 48, 166, 137, 34, 35, 66, 112, 67, 175, 72, 166, 225, 50, 49, 28, 197, 105, 187, 175, 221, 179, 223, 176, 167, 144, 4, 61, 73, 177, 139, 235, 183, 50, 123, 143, 53, 71, 48, 123, 92, 32, 108, 109, 72, 97, 230, 168, 79, 234, 164, 200, 179, 32, 229, 57, 190, 230, 83, 196, 125, 153, 181, 228, 82, 50, 79, 69, 31, 148, 169, 139, 234, 201, 238, 161, 185, 42, 171, 109, 50, 5, 168, 149, 65, 132, 15, 171, 67, 156, 194, 52, 246, 84, 102, 250, 191, 86, 181, 58, 150, 211, 152, 91, 221, 37] }) }
+cc 81d67fdba5d3d26fe6a434f3250028ad11efa8690605dcc1e729b9093d89319c # shrinks to ipv4_header = Ipv4Header { dscp: Ipv4Dscp(45), ecn: Ipv4Ecn(0), total_len: 35920, identification: 26558, dont_fragment: false, more_fragments: false, fragment_offset: IpFragOffset(3059), time_to_live: 229, protocol: 140 (Shim6 - Shim6 Protocol), header_checksum: 29111, source: [0, 0, 0, 12], destination: [13, 185, 25, 66], options: [] }, ipv4_exts = Ipv4Extensions { auth: None }, ipv6_header = Ipv6Header { traffic_class: 96, flow_label: Ipv6FlowLabel(567850), payload_length: 15281, next_header: 129 (IPLT), hop_limit: 124, source: [123, 234, 234, 217, 50, 16, 172, 178, 115, 31, 106, 193, 161, 59, 216, 72], destination: [73, 48, 180, 251, 82, 7, 64, 168, 191, 3, 137, 226, 189, 213, 123, 92] }, mut ipv6_exts = Ipv6Extensions { hop_by_hop_options: Some(Ipv6RawExtHeader { next_header: 60 (IPv6-Opts - Destination Options for IPv6), payload: [65, 49, 230, 46, 53, 132, 222, 119, 14, 131, 44, 89, 151, 176, 149, 43, 138, 235, 188, 94, 138, 133, 129, 1, 200, 242, 114, 42, 112, 227, 62, 255, 60, 209, 162, 71, 140, 16, 124, 213, 51, 135, 158, 229, 159, 209, 175, 114, 164, 231, 131, 107, 231, 31, 38, 205, 233, 170, 238, 239, 98, 56, 112, 253, 15, 127, 117, 244, 62, 127, 46, 215, 170, 51, 150, 173, 22, 187, 12, 97, 82, 50, 175, 99, 248, 50, 111, 40, 207, 62, 193, 64, 203, 228, 113, 223, 43, 136, 122, 117, 58, 214, 9, 186, 14, 64, 59, 158, 16, 39, 14, 85, 6, 150, 223, 148, 96, 136, 94, 18, 102, 159, 59, 106, 244, 75, 2, 0, 161, 45, 68, 218, 207, 182, 228, 237, 72, 138, 230, 238, 239, 230, 89, 201, 228, 29, 13, 158, 92, 245, 234, 133, 45, 133, 231, 107, 226, 213, 41, 241, 184, 170, 216, 14, 176, 161, 155, 132, 248, 90, 182, 107, 71, 237, 93, 67, 113, 106, 20, 20, 235, 102, 28, 156, 213, 219, 37, 140, 96, 13, 193, 174, 31, 161, 49, 148, 168, 183, 164, 101, 44, 63, 71, 42, 216, 226, 155, 4, 188, 72, 203, 56, 43, 188, 68, 56, 213, 103, 135, 176, 145, 174, 173, 65, 167, 242, 240, 33, 102, 48, 252, 254, 49, 115, 230, 134, 35, 202, 155, 133, 36, 83, 45, 169, 92, 249, 37, 35, 9, 154, 234, 138, 182, 178, 236, 96, 219, 77, 38, 245, 209, 187, 93, 203, 210, 13, 42, 228, 119, 8, 210, 132, 243, 166, 200, 41, 105, 233, 183, 154, 81, 35, 8, 243, 160, 211, 180, 221, 223, 139, 31, 104, 248, 156, 16, 53, 193, 72, 73, 152, 105, 237, 119, 154, 114, 25, 108, 147, 135, 82, 62, 219, 228, 245, 114, 129, 65, 106, 13, 59, 226, 123, 182, 0, 115, 189, 110, 211, 55, 248, 232, 205, 51, 185, 103, 249, 76, 200, 221, 88, 220, 76, 39, 151, 116, 179, 237, 54, 7, 226, 165, 242, 221, 218, 248, 253, 157, 72, 248, 126, 29, 38, 110, 113, 110, 10, 38, 157, 64, 249, 250, 225, 129, 104, 130, 17, 194, 159, 255, 248, 61, 214, 62, 83, 47, 142, 3, 128, 202, 110, 7, 150, 92, 233, 201, 187, 40, 214, 250, 80, 129, 242, 8, 100, 145, 141, 172, 129, 12, 64, 43, 176, 178, 82, 152, 176, 180, 11, 62, 238, 162, 62, 113, 132, 102, 80, 84, 25, 72, 219, 133, 159, 16, 96, 58, 104, 114, 99, 239, 198, 4, 101, 20, 21, 209, 94, 102, 239, 58, 125, 85, 30, 238, 10, 156, 155, 90, 242, 196, 197, 187, 60, 63, 232, 231, 101, 207, 162, 218, 14, 140, 152, 213, 176, 122, 67, 20, 247, 176, 138, 124, 179, 86, 126, 239, 174, 109, 132, 64, 138, 232, 253, 101, 147, 112, 29, 160, 72, 106, 11, 33, 149, 37, 60, 223, 188, 196, 114, 5, 20, 149, 9, 186, 90, 233, 14, 227, 95, 72, 89, 220, 112, 205, 108, 210, 176, 35, 150, 178, 194, 85, 187, 210, 55, 36, 38, 221, 214, 48, 43, 13, 234, 58, 24, 35, 232, 39, 95, 181, 64, 17, 203, 142, 79, 231, 73, 118, 232, 238, 152, 67, 236, 75, 92, 148, 225, 128, 90, 223, 241, 82, 174, 241, 142, 197, 160, 66, 151, 34, 18, 249, 42, 64, 253, 63, 118, 21, 238, 245, 30, 157, 83, 77, 124, 226, 170, 41, 20, 184, 218, 163, 106, 124, 23, 252, 18, 184, 127, 50, 29, 99, 162, 36, 85, 244, 170, 145, 195, 80, 162, 152, 112, 214, 148, 58, 253, 89, 207, 20, 37, 157, 170, 9, 50, 105, 81, 189, 118, 40, 27, 39, 246, 146, 194, 165, 77, 19, 221, 194, 108, 254, 88, 217, 192, 116, 208, 0, 108, 151, 191, 192, 116, 68, 46, 222, 66, 237, 238, 94, 23, 60, 209, 180, 132, 188, 117, 144, 231, 223, 176, 248, 100, 158, 78, 156, 140, 66, 214, 235, 52, 82, 200, 33, 117, 232, 104, 77, 216, 145, 140, 251, 168, 237, 178, 167, 139, 73, 243, 158, 207, 112, 167, 122, 21, 173, 191, 108, 173, 248, 80, 147, 18, 255, 28, 22, 248, 227, 22, 174, 34, 32, 185, 146, 246, 212, 15, 216, 62, 127, 171, 66, 28, 193, 77, 52, 217, 35, 18, 38, 163, 204, 210, 43, 167, 17, 74, 220, 97, 171, 116, 132, 40, 112, 68, 146, 185, 228, 56, 122, 91, 201, 73, 6, 13, 169, 252, 215, 213, 52, 140, 32, 240, 72, 182, 55, 137, 26, 142, 10, 161, 97, 151, 123, 171, 29, 13, 93, 210, 83, 128, 121, 152, 148, 131, 221, 153, 210, 45, 0, 24, 73, 15, 212, 139, 243, 208, 38, 79, 36, 118, 248, 211, 147, 254, 115, 179, 93, 132, 245, 26, 216, 240, 107, 245, 16, 1, 183, 138, 44, 148, 181, 137, 238, 251, 40, 212, 254, 43, 158, 143, 80, 104, 22, 37, 226, 88, 12, 3, 8, 224, 21, 236, 255, 36, 125, 239, 183, 138, 231, 222, 163, 91, 178, 146, 64, 0, 156, 197, 80, 188, 204, 31, 62, 47, 214, 68, 91, 93, 123, 223, 31, 136, 181, 125, 195, 127, 128, 98, 57, 223, 199, 195, 113, 135, 135, 208, 160, 157, 160, 244, 146, 137, 109, 219, 229, 205, 6, 200, 194, 210, 144, 11, 217, 120, 53, 213, 179, 13, 1, 221, 205, 192, 64, 184, 211, 103, 25, 215, 17, 24, 30, 184, 169, 245, 128, 63, 124, 173, 61, 147, 173, 157, 248, 13, 2, 179, 37, 54, 171, 147, 97, 185, 72, 239, 67, 97, 104, 13, 113, 223, 141, 78, 241, 193, 82, 253, 217, 9, 89, 147, 203, 251, 217, 190, 168, 134, 81, 12, 214, 176, 196, 1, 108, 141, 224, 129, 9, 157, 28, 153, 13, 51, 244, 35, 236, 97, 180, 121, 9, 203, 109, 195, 124, 181, 139, 34, 53, 211, 174, 85, 207, 188, 235, 28, 210, 215, 11, 14, 251, 219, 147, 4, 81, 226, 5, 196, 91, 37, 183, 48, 58, 183, 146, 21, 27, 100, 67, 187, 68, 3, 241, 241, 13, 230, 135, 113, 228, 126, 212, 236, 126, 163, 152, 1, 220, 204, 89, 34, 27, 216, 122, 228, 140, 39, 26, 2, 72, 9, 140, 226, 40, 197, 125, 64, 103, 171, 238, 61, 11, 56, 78, 157, 143, 212, 231, 66, 146, 140, 153, 37, 3, 155, 24, 169, 56, 40, 59, 246, 13, 12, 154, 119, 121, 72, 115, 246, 162, 161, 208, 120, 12, 140, 213, 132, 65, 127, 29, 17, 140, 17, 121, 195, 111, 141, 70, 27, 142, 10, 249, 106, 212, 151, 240, 91, 213, 237, 198, 224, 47, 133, 193, 179, 36, 76, 226, 148, 151, 216, 242, 47, 135, 119, 103, 90, 125, 184, 161, 94, 192, 4, 232, 72, 248, 219, 93, 138, 170, 252, 135, 16, 25, 239, 26, 170, 62, 36, 181, 7, 140, 42, 170, 101, 63, 223, 169, 98, 175, 121, 165, 15, 121, 38, 254, 59, 154, 107, 194, 100, 3, 160, 215, 74, 43, 74, 140, 242, 254, 156, 241, 161, 73, 126, 87, 168, 83, 100, 179, 161, 199, 72, 240, 242, 32, 197, 118, 67, 140, 29, 51, 140, 143, 127, 125, 95, 102, 140, 6, 122, 68, 204, 101, 172, 177, 47, 121, 50, 118, 126, 84, 102, 39, 146, 39, 47, 114, 200, 48, 232, 192, 179, 124, 2, 198, 88, 50, 231, 186, 16, 96, 181, 42, 160, 226, 130, 231, 65, 109, 250, 237, 159, 24, 128, 144, 133, 180, 212, 126, 99, 138, 158, 195, 163, 246, 136, 107, 120, 41, 188, 153, 94, 87, 73, 61, 177, 166, 72, 56, 226, 115, 82, 51, 108, 160, 62, 154, 105, 163, 59, 251, 72, 230, 91, 77, 3, 9, 155, 255, 45, 201, 71, 104, 226, 58, 181, 159, 23, 7, 133, 243, 254, 240, 61, 213, 215, 3, 211, 22, 21, 189, 133, 25, 160, 218, 56, 107, 13, 254, 166, 255, 19, 28, 113, 217, 174, 244, 232, 193, 27, 139, 166, 84, 205, 133, 65, 20, 250, 64, 235, 172, 76, 12, 135, 217, 242, 197, 233, 109, 124, 203, 65, 221, 228, 70, 107, 129, 177, 35, 37, 101, 202, 183, 63, 179, 168, 189, 49, 45, 222, 102, 146, 185, 6, 65, 50, 246, 219, 102, 55, 21, 42, 136, 196, 105, 168, 61, 53, 74, 110, 183, 15, 6, 240, 203, 101, 103, 116, 114, 80, 103, 177, 32, 176, 5, 188, 18, 171, 223, 55, 144, 98, 74, 204, 237, 53, 251, 137, 152, 1, 130, 76, 164, 61, 222, 67, 255, 68, 53, 213, 194, 72, 198, 62, 19, 82, 64, 138, 9, 132, 176, 232, 139, 47, 112, 163, 96, 190, 242, 22, 132, 5, 230, 59, 219, 94, 244, 237, 211, 152, 229, 162, 119, 23, 76, 93, 3, 172, 60, 201, 167, 178, 16, 231, 193, 2, 226, 95, 12, 203, 201, 159, 41, 139, 199, 172, 61, 124, 224, 95, 184, 184, 25, 244, 131, 148, 112, 234, 121, 0, 85, 199, 131, 95, 77, 189, 237, 110, 180, 6, 98, 24, 111, 222, 10, 189, 233, 79, 114, 129, 224, 89, 224, 37, 99, 63, 254, 31, 142, 215, 53, 204, 55, 193, 163, 104, 195, 78, 182, 60, 90, 132, 250, 195, 51, 78, 253, 146, 243, 33, 16, 211, 118, 191, 218, 15, 80, 87, 208, 248, 58, 12, 154, 21, 181, 68, 113, 137, 225, 0, 163, 176, 168, 83, 35, 67, 128, 223, 140, 235, 140, 75, 215, 86, 153, 254, 120, 229, 227, 138, 216, 119, 205, 235, 150, 158, 222, 93, 54, 23, 220, 107, 22, 139, 210, 152, 19, 227, 45, 155, 6, 252, 158, 178, 172, 14, 173, 150, 113, 250, 152, 182, 202, 185, 176, 164, 210, 122, 192, 189, 162, 84, 133, 66, 126, 198, 158, 162, 182, 158, 255, 37, 100, 247, 227, 33, 58, 87, 218, 104, 147, 47, 182, 0, 93, 65, 68, 7, 168, 169, 52, 182] }), destination_options: Some(Ipv6RawExtHeader { next_header: 43 (IPv6-Route - Routing Header for IPv6), payload: [225, 88, 122, 111, 189, 104, 252, 254, 225, 201, 108, 106, 94, 37, 20, 107, 200, 193, 109, 13, 8, 246, 138, 253, 149, 13, 43, 229, 45, 186, 98, 123, 13, 87, 215, 162, 100, 147, 32, 4, 254, 196, 253, 207, 19, 19, 136, 216, 60, 44, 38, 31, 195, 21, 236, 187, 223, 206, 67, 157, 75, 126, 176, 113, 41, 89, 244, 30, 219, 225, 23, 193, 253, 35, 143, 90, 36, 19, 50, 163, 182, 247, 240, 158, 191, 78, 207, 245, 186, 102, 190, 130, 11, 34, 179, 67, 110, 53, 93, 142, 98, 172, 163, 174, 19, 122, 180, 175, 123, 49, 117, 94, 20, 22, 190, 190, 232, 178, 22, 210, 137, 181, 94, 99, 59, 129, 196, 215, 65, 9, 247, 144, 248, 248, 254, 26, 68, 52, 117, 9, 104, 161, 81, 170, 117, 196, 136, 0, 113, 243, 149, 144, 7, 246, 38, 186, 13, 226, 195, 2, 205, 243, 150, 10, 81, 194, 15, 54, 246, 127, 14, 241, 132, 84, 161, 147, 197, 250, 193, 211, 47, 3, 59, 199, 140, 71, 243, 250, 87, 75, 191, 238, 113, 40, 183, 95, 168, 45, 136, 125, 45, 91, 150, 161, 244, 148, 194, 159, 33, 203, 74, 54, 109, 161, 112, 188, 54, 236, 134, 195, 97, 216, 16, 163, 195, 168, 234, 145, 56, 158, 78, 203, 237, 228, 90, 238, 116, 204, 127, 234, 249, 65, 2, 188, 113, 105, 35, 206, 76, 175, 250, 86, 203, 167, 27, 159, 39, 203, 120, 240, 172, 16, 199, 160, 231, 2, 11, 101, 163, 19, 16, 38, 116, 148, 50, 205, 154, 180, 232, 248, 184, 60, 54, 45, 186, 160, 138, 134, 27, 201, 21, 44, 11, 86, 48, 127, 128, 144, 183, 84, 176, 165, 103, 8, 4, 217, 0, 116, 89, 228, 58, 221, 156, 14, 98, 67, 4, 96, 105, 63, 67, 154, 98, 116, 22, 71, 101, 172, 227, 227, 135, 13, 59, 60, 150, 89, 113, 12, 76, 131, 40, 33, 220, 191, 225, 204, 220, 207, 235, 157, 167, 135, 170, 79, 123, 55, 90, 64, 10, 101, 148, 81, 32, 81, 64, 11, 47, 21, 11, 18, 144, 231, 139, 227, 234, 176, 93, 10, 42, 171, 117, 253, 97, 184, 123, 123, 15, 48, 46, 144, 154, 23, 74, 47, 225, 245, 27, 145, 200, 98, 20, 83, 127, 15, 79, 53, 203, 19, 202, 29, 165, 3, 187, 56, 159, 165, 28, 128, 84, 87, 88, 38, 177, 26, 52, 78, 248, 24, 186, 221, 195, 215, 50, 180, 168, 83, 13, 202, 224, 155, 3, 65, 143, 112, 147, 72, 237, 152, 68, 104, 117, 171, 119, 19, 45, 213, 127, 135, 64, 106, 107, 129, 154, 143, 169, 24, 153, 13, 114, 25, 113, 4, 115, 91, 183, 141, 34, 113, 116, 216, 203, 136, 146, 165, 112, 15, 93, 143, 174, 245, 1, 72, 168, 241, 182, 28, 193, 232, 147, 240, 133, 219, 118, 91, 139, 133, 88, 212, 85, 10, 249, 175, 87, 134, 101, 103, 112, 48, 90, 180, 130, 105, 45, 253, 212, 148, 104, 59, 0, 249, 133, 74, 97, 182, 231, 226, 225, 24, 248, 157, 120, 132, 223, 193, 207, 80, 39, 23, 89, 126, 80, 205, 16, 170, 113, 121, 63, 241, 32, 128, 2, 25, 162, 35, 184, 13, 38, 81, 140, 132, 160, 172, 148, 136, 139, 1, 98, 39, 23, 181, 108, 237, 142, 237, 16, 210, 61, 53, 160, 40, 57, 17, 3, 190, 101, 54, 214, 213, 173, 48, 223, 196, 217, 35, 139, 179, 226, 119, 133, 106, 60, 140, 61, 143, 158, 25, 72, 177, 49, 61, 137, 200, 189, 175, 62, 2, 83, 46, 154, 101, 49, 119, 93, 125, 246, 164, 68, 28, 242, 15, 177, 72, 55, 129, 213, 219, 203, 77, 240, 7, 224, 150, 12, 82, 55, 135, 99, 172, 238, 49, 85, 224, 5, 15, 220, 22, 227, 132, 167, 126, 199, 89, 131, 132, 97, 39, 137, 127, 137, 105, 216, 156, 208, 124, 146, 236, 242, 234, 241, 108, 32, 14, 135, 55, 152, 233, 3, 196, 203, 123, 39, 90, 8, 229, 202, 52, 138, 188, 220, 241, 192, 243, 168, 116, 166, 84, 12, 250, 59, 164, 1, 170, 114, 178, 28, 177, 28, 178, 122, 180, 32, 10, 86, 93, 127, 35, 162, 239, 69, 73, 102, 235, 51, 36, 120, 64, 183, 23, 210, 176, 227, 161, 15, 57, 3, 113, 105, 133, 255, 166, 208, 131, 173, 68, 182, 108, 15, 116, 119, 152, 222, 50, 197, 169, 44, 14, 231, 255, 243, 11, 25, 76, 55, 4, 241, 28, 105, 185, 163, 125, 229, 95, 255, 31, 244, 3, 141, 116, 227, 169, 76, 171, 4, 63, 133, 213, 38, 87, 179, 238, 73, 156, 176, 197, 92, 243, 241, 253, 166, 9, 144, 82, 120, 126, 81, 165, 100, 201, 24, 204, 78, 24, 117, 99, 29, 143, 222, 164, 236, 52, 107, 51, 67, 62, 143, 162, 67, 229, 248, 203, 0, 135, 206, 13, 240, 131, 42, 156, 135, 133, 157, 1, 127, 161, 150, 103, 137, 17, 117, 39, 197, 243, 190, 135, 17, 223, 207, 161, 108, 1, 240, 101, 114, 69, 65, 227, 72, 123, 90, 243, 216, 196, 16, 56, 21, 158, 181, 251, 101, 124, 136, 85, 23, 153, 28, 78, 75, 102, 197, 247, 43, 8, 69, 204, 4, 111, 157, 74, 226, 251, 252, 30, 81, 33, 60, 82, 103, 205, 59, 123, 221, 3, 71, 137, 9, 217, 239, 108, 243, 7, 111, 11, 60, 75, 85, 126, 3, 72, 8, 70, 73, 254, 181, 71, 115, 24, 70, 157, 182, 66, 14, 24, 110, 113, 127, 181, 0, 83, 189, 204, 147, 217, 120, 212, 75, 189, 243, 72, 0, 131, 245, 190, 139, 172, 247, 75, 147, 19, 149, 57, 181, 100, 248, 154, 81, 24, 216, 158, 179, 213, 176, 46, 3, 253, 24, 118, 158, 180, 206, 156, 253, 157, 120, 232, 34, 94, 220, 149, 13, 255, 120, 156, 172, 4, 189, 57, 236, 20, 168, 210, 98, 229, 103, 235, 240, 190, 249, 216, 164, 138, 66, 165, 152, 141, 7, 66, 215, 177, 123, 231, 230, 34, 71, 9, 160, 69, 239, 62, 19, 65, 67, 140, 82, 180, 66, 250, 23, 141, 138, 179, 162, 180, 189, 245, 122, 77, 188, 180, 93, 87, 46, 194, 39, 154, 194, 93, 217, 212, 84, 10, 160, 96, 200, 45, 158, 237, 197, 130, 129, 249, 221, 142, 245, 134, 56, 137, 23, 124, 202, 112, 185, 235, 219, 36, 12, 60, 40, 238, 159, 68, 90, 208, 137, 217, 6, 164, 56, 160, 117, 38, 97, 247, 29, 157, 14, 154, 60, 253, 38, 1, 138, 120, 79, 31, 67, 51, 223, 49, 197, 89, 210, 10, 84, 243, 15, 13, 64, 232, 46, 231, 236, 195, 121, 144, 80, 102, 25, 151, 106, 184, 191, 235, 117, 126, 208, 212, 42, 143, 113, 201, 92, 12, 61, 77, 132, 143, 54, 132, 150, 4, 43, 237, 212, 70, 95, 63, 60, 155, 231, 219, 103, 58, 5, 157, 155, 135, 18, 160, 54, 12, 101, 188, 25, 50, 66, 198, 58, 196, 232, 2, 24, 60, 217, 84, 71, 69, 175, 3, 144, 182, 187, 53, 223, 107, 102, 160, 24, 174, 151, 56, 223, 178, 66, 49, 211, 45, 174, 43, 154, 222, 61, 169, 52, 215, 42, 60, 144, 134, 92, 61, 255, 103, 242, 21, 65, 217, 175, 2, 52, 204, 50, 181, 29, 83, 224, 118, 2, 122, 243, 110, 57, 106, 60, 140, 138, 114, 19, 152, 100, 21, 146, 235, 69, 100, 132, 242, 115, 13, 232, 116, 34, 197, 133, 2, 246, 173, 60, 141, 12, 136, 77, 1, 91, 35, 36, 192, 155, 126, 248, 29, 141, 62, 92, 252, 139, 200, 150, 148, 80, 159, 187, 145, 151, 118, 243, 10, 46, 126, 203, 104, 12, 146, 141, 6, 73, 39, 195, 135, 196, 188, 64, 35, 212, 189, 49, 236, 126, 230, 157, 209, 10, 255, 128, 226, 176, 150, 43, 106, 107, 108, 175, 102, 232, 47, 253, 207, 206, 254, 73, 187, 110, 32, 186, 197, 200, 35, 20, 107, 56, 249, 179, 79, 148, 85, 202, 91, 128, 182, 11, 53, 96, 140, 35, 102, 142, 89, 155, 135, 20, 197, 81, 211, 134, 240, 170, 4, 217, 143, 59, 178, 45, 208, 10, 162, 153, 231, 126, 63, 45, 153] }), routing: Some(Ipv6RoutingExtensions { routing: Ipv6RawExtHeader { next_header: 44 (IPv6-Frag - Fragment Header for IPv6), payload: [41, 114, 193, 71, 183, 121, 174, 25, 173, 80, 132, 71, 219, 139, 73, 91, 168, 187, 166, 25, 56, 209, 167, 60, 44, 35, 44, 107, 86, 88, 28, 206, 142, 117, 35, 147, 51, 42, 157, 153, 122, 246, 46, 48, 247, 118, 77, 129, 16, 151, 140, 74, 124, 228, 242, 100, 174, 92, 95, 89, 118, 96, 212, 226, 179, 1, 230, 207, 8, 82, 111, 220, 239, 201, 180, 97, 54, 25, 7, 241, 54, 108, 139, 13, 5, 113, 23, 68, 102, 65, 251, 166, 24, 155, 94, 209, 18, 94, 76, 100, 235, 74, 250, 161, 247, 173, 62, 249, 233, 90, 111, 223, 3, 204, 49, 207, 56, 50, 63, 73, 60, 3, 88, 169, 34, 188, 105, 38, 209, 113, 11, 246, 76, 24, 141, 72, 227, 225, 156, 232, 234, 211, 246, 26, 197, 192, 251, 216, 201, 48, 200, 195, 244, 253, 182, 50, 146, 135, 11, 12, 253, 79, 2, 50, 76, 113, 9, 15, 252, 228, 229, 180, 71, 228, 216, 178, 108, 241, 186, 120, 135, 154, 50, 108, 22, 210, 181, 39, 108, 9, 175, 235, 200, 23, 37, 29, 47, 200, 1, 88, 192, 117, 45, 254, 118, 17, 150, 64, 150, 215, 212, 128, 115, 17, 83, 104, 33, 239, 235, 7, 75, 72, 204, 184, 232, 69, 91, 82, 114, 76, 173, 33, 111, 28, 177, 7, 32, 255, 142, 208, 6, 242, 111, 32, 160, 106, 3, 230, 99, 9, 200, 62, 223, 228, 46, 123, 70, 151, 83, 86, 86, 231, 242, 204, 191, 185, 227, 191, 36, 137, 6, 98, 39, 4, 192, 196, 36, 4, 28, 87, 190, 122, 239, 158, 79, 70, 67, 195, 145, 108, 24, 80, 253, 10, 122, 45, 69, 131, 128, 12, 158, 199, 237, 141, 51, 187, 216, 188, 39, 112, 137, 199, 160, 31, 68, 164, 129, 63, 155, 93, 238, 61, 122, 103, 217, 168, 243, 65, 42, 87, 173, 237, 195, 125, 30, 21, 10, 65, 196, 103, 26, 95, 253, 198, 223, 127, 209, 148, 186, 208, 119, 222, 138, 58, 7, 85, 36, 49, 183, 66, 52, 209, 43, 123, 13, 1, 121, 117, 159, 19, 190, 229, 198, 230, 176, 50, 64, 95, 64, 75, 21, 88, 213, 108, 93, 39, 78, 154, 108, 177, 101, 236, 225, 213, 240, 57, 19, 97, 0, 18, 23, 203, 23, 97, 134, 208, 232, 174, 47, 35, 28, 165, 214, 10, 160, 70, 159, 201, 166, 255, 13, 31, 132, 174, 71, 123, 198, 136, 117, 16, 44, 156, 29, 234, 177, 100, 32, 84, 232, 191, 161, 148, 21, 84, 6, 166, 230, 10, 255, 21, 81, 207, 182, 176, 239, 201, 140, 122, 164, 5, 230, 109, 11, 230, 183, 130, 223, 188, 202, 74, 108, 77, 247, 84, 148, 100, 94, 229, 202, 170, 87, 80, 180, 132, 67, 246, 87, 203, 18, 79, 128, 208, 70, 132, 155, 76, 95, 170, 22, 209, 163, 155, 57, 130, 194, 17, 2, 91, 138, 23, 158, 216, 159, 142, 8, 223, 240, 26, 192, 108, 177, 226, 23, 156, 89, 85, 210, 55, 6, 76, 128, 22, 110, 149, 47, 93, 13, 23, 183, 95, 200, 93, 159, 56, 112, 238, 141, 235, 248, 107, 142, 67, 207, 8, 40, 119, 37, 0, 172, 26, 0, 54, 225, 40, 231, 109, 75, 255, 98, 157, 227, 58, 53, 192, 180, 101, 40, 162, 78, 128, 124, 17, 81, 57, 236, 88, 148, 254, 135, 87, 91, 44, 164, 71, 105, 17, 66, 227, 240, 208, 37, 114, 0, 213, 117, 8, 217, 216, 92, 23, 152, 191, 187, 125, 34, 243, 64, 109, 39, 125, 161, 201, 188, 220, 159, 15, 180, 140, 238, 55, 171, 182, 155, 68, 33, 179, 40, 152, 219, 70, 53, 100, 217, 89, 228, 86, 142, 89, 34, 1, 238, 22, 96, 133, 31, 245, 41, 174, 148, 132, 51, 230, 238, 160, 2, 183, 221, 34, 97, 231, 215, 77, 115, 0, 135, 242, 73, 234, 237, 137, 173, 81, 15, 114, 191, 167, 68, 143, 64, 152, 3, 241, 164, 33, 227, 240, 202, 35, 16, 85, 112, 205, 65, 177, 237, 57, 94, 45, 41, 125, 215, 186, 134, 19, 139, 6, 200, 254, 6, 11, 68, 220, 129, 152, 65, 252, 177, 165, 151, 102, 172, 234, 39, 27, 55, 231, 228, 154, 159, 81, 41, 217, 78, 72, 108, 113, 191, 54, 154, 155, 204, 113, 217, 234, 217, 121, 196, 33, 87, 17, 109, 72, 49, 76, 219, 11, 233, 246, 54, 171, 96, 202, 34, 138, 173, 93, 208, 86, 70, 100, 138, 63, 54, 166, 95, 114, 149, 119, 5, 131, 38, 44, 116, 125, 39, 133, 14, 91, 149, 34, 147, 53, 212, 108, 191, 225, 192, 163, 3, 92, 68, 216, 180, 24, 91, 58, 217, 206, 60, 186, 107, 219, 55, 246, 6, 200, 199, 247, 3, 76, 37, 219, 113, 99, 244, 110, 59, 152, 55, 8, 66, 208, 116, 146, 245, 227, 176, 24, 104, 104, 22, 7, 20, 64, 223, 171, 197, 123, 41, 244, 80, 73, 126, 143, 215, 211, 136, 69, 234, 219, 81, 156, 12, 148, 76, 39, 247, 72, 97, 211, 35, 188, 2, 30, 185, 228, 129, 165, 168, 69, 237, 52, 5, 56, 58, 116, 246, 68, 109, 81, 168, 46, 41, 168, 69, 255, 250, 246, 226, 81, 139, 144, 235, 96, 171, 171, 202, 237, 173, 35, 111, 157, 205, 72, 4, 132, 5, 19, 178, 68, 100, 151, 176, 174, 88, 225, 156, 54, 186, 203, 77, 109, 91, 61, 26, 106, 172, 82, 3, 67, 34, 172, 185, 71, 19, 220, 63, 236, 195, 53, 149, 179, 167, 126, 109, 191, 190, 204, 107, 239, 118, 5, 21, 150, 97, 73, 34, 236, 63, 159, 63, 39, 190, 205, 130, 28, 174, 125, 89, 4, 32, 126, 179, 166, 192, 121, 107, 128, 115, 172, 48, 92, 2, 244, 23, 175, 174, 255, 81, 185, 46, 39, 164, 43, 210, 183, 212, 222, 10, 110, 122, 6, 31, 93, 3, 37, 65, 82, 153, 29, 242, 126, 153, 113, 9, 241, 18, 188, 207, 111, 125, 63, 217, 25, 199, 223, 160, 37, 64, 148, 73, 43, 179, 129, 110, 201, 44, 122, 146, 40, 194, 147, 106, 69, 136, 88, 179, 5, 120, 190, 111, 30, 192, 183, 193, 133, 251, 207, 103, 120, 150, 184, 38, 101, 49, 31, 219, 92, 138, 164, 200, 119, 184, 160, 210, 201, 117, 144, 144, 29, 30, 61, 178, 61, 167, 53, 211, 146, 237, 44, 130, 138, 201, 198, 72, 120, 56, 172, 145, 178, 110, 244, 2, 83, 173, 45, 65, 128, 237, 8, 105, 30, 235, 240, 124, 218, 55, 255, 221, 125, 154, 232, 72, 190, 105, 192, 16, 177, 26, 169, 243, 73, 99, 107, 75, 102, 62, 95, 29, 255, 207, 30, 93, 77, 155, 161, 75, 67, 148, 250, 115, 67, 98, 241] }, final_destination_options: None }), fragment: Some(Ipv6FragmentHeader { next_header: 51 (AH - Authentication Header), fragment_offset: IpFragOffset(1843), more_fragments: true, identification: 4118775582 }), auth: Some(IpAuthHeader { next_header: 17 (UDP - User Datagram), spi: 906581493, sequence_number: 2469050049, raw_icv: [235, 210, 82, 43, 88, 108, 79, 35, 37, 35, 190, 134, 79, 52, 155, 106, 112, 184, 166, 225, 142, 219, 91, 41, 88, 129, 63, 180, 146, 68, 38, 13, 79, 125, 97, 221, 162, 203, 5, 170, 166, 245, 37, 151, 42, 235, 95, 73, 34, 9, 174, 1, 22, 244, 83, 238, 210, 103, 119, 172, 157, 35, 5, 178, 230, 180, 198, 135, 24, 167, 102, 27, 127, 150, 138, 135, 198, 212, 0, 189, 66, 92, 179, 243, 15, 200, 12, 53, 19, 236, 171, 247, 98, 172, 128, 210, 52, 211, 148, 198, 112, 99, 191, 25, 25, 95, 36, 50, 31, 225, 7, 146, 46, 26, 56, 185, 77, 85, 219, 241, 60, 149, 131, 76, 49, 86, 101, 240, 116, 169, 152, 198, 214, 8, 164, 113, 42, 41, 72, 222, 112, 35, 108, 22, 141, 210, 59, 152, 125, 220, 195, 188, 27, 135, 43, 124, 149, 123, 141, 220, 177, 89, 57, 20, 82, 118, 91, 85, 125, 232, 175, 39, 252, 43, 153, 137, 94, 102, 106, 70, 106, 69, 99, 140, 16, 249, 156, 126, 96, 173, 236, 40, 163, 155, 105, 193, 33, 53, 210, 100, 35, 94, 215, 174, 82, 210, 23, 65, 166, 215, 74, 97, 17, 128, 10, 43, 103, 226, 177, 118, 121, 32, 110, 132, 27, 127, 8, 113, 65, 92, 129, 160, 162, 117, 112, 81, 156, 215, 206, 102, 29, 51, 227, 18, 75, 133, 252, 250, 41, 152, 36, 168, 165, 109, 4, 190, 141, 232, 153, 99, 107, 165, 58, 139, 144, 122, 46, 76, 214, 62, 250, 207, 30, 9, 108, 31, 34, 34, 21, 240, 110, 173, 28, 69, 116, 52, 72, 208, 126, 114, 243, 202, 254, 48, 90, 5, 186, 85, 113, 208, 88, 197, 3, 239, 132, 73, 128, 235, 191, 251, 49, 179, 25, 162, 2, 213, 247, 242, 248, 12, 252, 126, 206, 132, 202, 136, 35, 17, 59, 1, 122, 65, 60, 78, 94, 48, 70, 98, 224, 4, 205, 219, 139, 65, 100, 108, 246, 162, 190, 53, 48, 246, 124, 26, 132, 224, 231, 36, 227, 31, 90, 52, 35, 28, 252, 8, 194, 246, 98, 233, 178, 185, 130, 153, 220, 32, 169, 142, 39, 130] }) }
diff --git a/proptest-regressions/internet/lax_ipv4_slice.txt b/proptest-regressions/internet/lax_ipv4_slice.txt
new file mode 100644
index 0000000..4c38358
--- /dev/null
+++ b/proptest-regressions/internet/lax_ipv4_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 4724ddcae41fb2b83ddd860c5a2af55e5da54b5bf7a344cb018b37c7e1566921 # shrinks to v4 = Ipv4Header { dscp: Ipv4Dscp(19), ecn: Ipv4Ecn(3), total_len: 1385, identification: 26272, dont_fragment: true, more_fragments: true, fragment_offset: IpFragOffset(3574), time_to_live: 83, protocol: 36 (XTP - XTP), header_checksum: 50303, source: [0, 0, 0, 0], destination: [0, 99, 43, 14], options: [] }, v4_exts = Ipv4Extensions { auth: Some(IpAuthHeader { next_header: 76 (BR-SAT-MON - Backroom SATNET Monitoring), spi: 1975662188, sequence_number: 2239760872, raw_icv: [149, 42, 236, 203, 206, 127, 250, 146, 34, 212, 83, 211, 63, 59, 197, 24, 243, 51, 121, 242, 6, 58, 30, 213, 7, 57, 47, 36, 199, 171, 193, 6, 89, 77, 70, 234, 57, 24, 120, 65, 135, 210, 111, 93, 149, 12, 90, 25, 124, 10, 245, 131, 62, 171, 96, 34, 203, 3, 160, 81, 254, 240, 254, 121, 254, 166, 98, 226, 130, 81, 168, 50, 161, 31, 25, 221, 10, 132, 169, 126, 112, 215, 19, 208, 137, 123, 159, 114, 89, 236, 32, 170, 235, 242, 221, 105, 65, 252, 238, 35, 105, 238, 161, 70, 142, 180, 57, 148, 244, 140, 104, 255, 177, 102, 155, 81, 213, 241, 22, 10, 5, 54, 203, 135, 207, 222, 91, 252, 61, 224, 149, 197, 64, 231, 27, 57, 150, 234, 154, 115, 140, 168, 140, 31, 59, 210, 57, 254, 36, 118, 160, 123, 119, 199, 70, 161, 248, 63, 162, 143, 120, 67, 243, 19, 79, 78, 78, 94, 39, 253, 0, 124, 31, 45, 59, 11, 216, 187, 215, 37, 66, 3, 112, 93, 255, 226, 81, 22, 21, 220, 156, 47, 164, 95, 63, 99, 46, 166, 247, 15, 23, 43, 228, 1, 47, 135, 102, 241, 42, 178, 76, 53, 197, 143, 7, 121, 248, 233, 224, 149, 141, 145, 115, 47, 179, 104, 98, 53, 19, 14, 92, 231, 140, 61, 246, 100, 42, 224, 94, 104, 46, 243, 142, 216, 127, 188, 31, 173, 77, 3, 157, 233, 65, 211, 225, 98, 158, 34, 119, 76, 199, 167, 21, 109, 115, 99, 87, 164, 91, 144, 224, 55, 172, 148, 163, 166, 121, 233, 160, 65, 142, 67, 112, 230, 204, 3, 162, 29, 163, 179, 237, 30, 119, 180, 106, 15, 60, 157, 182, 123, 189, 110, 212, 146, 211, 4, 58, 180, 27, 112, 244, 167, 181, 233, 154, 220, 68, 164, 102, 140, 207, 135, 65, 151, 126, 56, 220, 203, 118, 86, 197, 146, 22, 182, 200, 181, 47, 97, 182, 93, 123, 99, 51, 158, 179, 40, 108, 227, 127, 3, 41, 164, 202, 5, 42, 170, 158, 245, 128, 238, 255, 158, 167, 162, 61, 6, 142, 212, 122, 151, 208, 143, 15, 206, 154, 28, 114, 156, 174, 219, 216, 73, 239, 53, 217, 137, 248, 106, 44, 50, 217, 69, 54, 57, 41, 61, 193, 155, 57, 232, 4, 184, 22, 129, 60, 109, 110, 140, 120, 178, 5, 167, 183, 47, 237, 140, 249, 134, 181, 14, 235, 43, 80, 150, 126, 121, 9, 63, 20, 109, 166, 47, 105, 195, 125, 225, 148, 127, 150, 211, 161, 244, 238, 216, 193, 215, 194, 7, 162, 70, 39, 108, 178, 6, 16, 166, 254, 18, 22, 180, 44, 128, 134, 254, 29, 122, 254, 152, 25, 154, 248, 115, 234, 137, 106, 205, 118, 38, 25, 203, 145, 5, 163, 45, 102, 68, 211, 71, 66, 135, 40, 77, 177, 249, 215, 23, 186, 8, 226, 173, 126, 212, 107, 192, 158, 234, 155, 41, 152, 107, 171, 7, 79, 71, 46, 213, 83, 41, 168, 85, 157, 143, 242, 43, 169, 151, 76, 151, 115, 30, 231, 137, 28, 194, 167, 73, 35, 79, 250, 91, 17, 148, 219, 208, 42, 101, 175, 72, 241, 236, 209, 241, 106, 233, 91, 184, 153, 95, 100, 41, 150, 56, 251, 179, 203, 78, 155, 86, 50, 143, 78, 224, 16, 30, 129, 212, 125, 244, 214, 178, 23, 151, 166, 178, 51, 121, 106, 118, 6, 71, 215, 222, 233, 95, 234, 17, 67, 69, 200, 213, 248, 81, 147, 169, 197, 195, 227, 48, 131, 136, 189, 87, 189, 95, 76, 209, 20, 110, 222, 233, 255, 59, 60, 253, 100, 166, 188, 119, 103, 239, 74, 137, 55, 200, 76, 69, 157, 97, 141, 133, 27, 183, 148, 70, 189, 228, 200, 87, 38, 211, 244, 63, 246, 166, 179, 88, 59, 44, 187, 36, 167, 16, 98, 57, 136, 0, 39, 253, 169, 81, 45, 193, 218, 217, 125, 115, 124, 27, 48, 94, 254, 99, 227, 50, 248, 159, 206, 244, 239, 51, 210, 161, 206, 74, 38, 64, 15, 93, 90, 197, 76, 232, 238, 98, 68, 177, 29, 115, 232, 74, 110, 163, 93, 10, 197, 237, 31, 16, 122, 23, 133, 152, 245, 131, 255, 27, 135, 115, 22, 27, 228, 175, 107, 18, 106, 179, 166, 58, 50, 10, 69, 125, 138, 49, 50, 111, 206, 118, 15, 83, 236, 199, 65, 100, 195, 91, 124, 155, 2, 101, 239, 227, 146, 16, 254, 75, 228, 149, 108, 67, 195, 194, 140, 187, 160, 213, 109, 141, 11, 184, 234, 96, 99, 45, 0, 31, 17, 138, 237, 0, 189, 64, 5, 51, 239, 23, 154, 251, 243, 83, 157, 200, 210, 31, 33, 102, 101, 107, 103, 60, 184, 240, 168, 74, 16, 22, 158, 84, 167, 232, 15, 55, 216, 217, 159, 250, 23, 50, 4, 44, 24, 243, 188, 231, 18, 183] }) }
diff --git a/proptest-regressions/internet/lax_ipv6_slice.txt b/proptest-regressions/internet/lax_ipv6_slice.txt
new file mode 100644
index 0000000..4219900
--- /dev/null
+++ b/proptest-regressions/internet/lax_ipv6_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 f0761b48eac05085c3fa2a6c5c20148568d377c333f962b765f2f185e1615430 # shrinks to ipv6_base = Ipv6Header { traffic_class: 148, flow_label: Ipv6FlowLabel(94032), payload_length: 38061, next_header: 10 (BBN-RCC-MON - BBN RCC Monitoring), hop_limit: 199, source: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 216, 64, 193, 21, 9] }, auth_base = IpAuthHeader { next_header: 70 (VISA - VISA Protocol), spi: 535744038, sequence_number: 2726038174, raw_icv: [101, 79, 87, 105, 27, 252, 79, 166, 200, 200, 250, 158, 215, 243, 207, 2, 14, 154, 125, 118, 37, 26, 26, 99, 165, 92, 245, 187, 220, 245, 106, 51, 81, 140, 144, 21, 252, 209, 168, 117, 214, 156, 133, 228, 83, 41, 203, 119, 213, 137, 26, 175, 163, 109, 132, 64, 101, 9, 59, 189, 37, 109, 143, 133, 88, 188, 109, 163, 26, 80, 121, 143, 209, 36, 34, 216, 233, 225, 33, 164, 132, 192, 168, 6, 116, 230, 117, 59, 145, 216, 87, 1, 229, 122, 31, 19, 52, 9, 253, 54, 40, 198, 159, 197, 234, 26, 156, 10, 9, 44, 183, 157, 171, 4, 2, 32, 82, 38, 79, 119, 179, 29, 164, 188, 189, 95, 79, 236, 248, 39, 244, 190, 160, 232, 50, 157, 3, 101, 243, 113, 142, 154, 244, 13, 44, 160, 185, 246, 110, 70, 184, 112, 89, 39, 8, 233, 102, 126, 227, 110, 80, 233, 70, 124, 220, 9, 226, 250, 142, 73, 153, 18, 255, 229, 148, 203, 76, 37, 211, 145, 252, 105, 156, 150, 42, 78, 90, 59, 83, 202, 43, 216, 64, 221, 135, 159, 34, 161, 26, 55, 18, 2, 125, 110, 71, 25, 149, 101, 69, 87, 122, 143, 190, 56, 83, 112, 4, 42, 239, 171, 36, 33, 229, 47, 231, 247, 2, 212, 193, 167, 119, 171, 223, 56, 205, 10, 150, 67, 15, 180, 54, 226, 71, 30, 69, 25, 38, 55, 117, 184, 229, 123, 135, 89, 220, 55, 55, 91, 18, 21, 81, 214, 42, 14, 240, 227, 95, 71, 190, 125, 47, 84, 245, 101, 102, 199, 195, 13, 154, 66, 234, 119, 135, 38, 242, 176, 185, 67, 73, 91, 76, 2, 73, 118, 131, 153, 178, 50, 96, 119, 158, 125, 186, 29, 8, 186, 16, 127, 65, 180, 234, 99, 36, 97, 115, 255, 210, 103, 122, 73, 10, 88, 108, 180, 146, 112, 160, 230, 151, 11, 133, 100, 158, 135, 55, 65, 244, 62, 166, 102, 228, 221, 147, 250, 145, 203, 127, 247, 247, 152, 1, 122, 98, 42, 31, 246, 167, 222, 149, 92, 223, 103, 147, 194, 194, 149, 99, 82, 151, 82, 29, 31, 42, 57, 175, 68, 115, 22, 79, 57, 162, 63, 191, 68, 171, 117, 140, 211, 5, 107, 161, 94, 248, 245, 38, 221, 216, 107, 243, 119, 100, 184, 138, 121, 112, 130, 97, 208, 237, 163, 5, 255, 27, 142, 20, 109, 177, 63, 91, 115, 118, 207, 39, 68, 110, 94, 251, 252, 39, 11, 189, 46, 72, 70, 177, 26, 94, 47, 52, 55, 197, 126, 106, 215, 184, 89, 79, 212, 85, 91, 13, 87, 42, 77, 26, 20, 142, 200, 110, 224, 14, 121, 109, 120, 194, 235, 255, 31, 67, 49, 161, 149, 37, 222, 165, 72, 88, 32, 96, 165, 20, 162, 107, 174, 157, 94, 84, 122, 224, 226, 88, 127, 64, 183, 138, 185, 126, 59, 229, 56, 219, 129, 162, 126, 215, 126, 147, 249, 215, 46, 80, 152, 251, 33, 199, 48, 218, 32, 60, 26, 154, 242, 163, 184, 50, 235, 147, 211, 207, 183, 239, 199, 192, 236, 106, 157, 240, 45, 205, 162, 42, 48, 154, 172, 47, 43, 133, 57, 124, 134, 29, 177, 254, 105, 128, 40, 233, 77, 131, 98, 198, 29, 97, 165, 19, 25, 197, 132, 223, 7, 25, 154, 241, 138, 16, 123, 232, 254, 117, 165, 1, 2, 11, 135, 89, 79, 93, 119, 217, 90, 0, 99, 48, 74, 7, 68, 185, 185, 8, 131, 11, 130, 126, 9, 179, 113, 11, 17, 212, 51, 180, 161, 48, 163, 113, 53, 96, 73, 112, 133, 82, 72, 10, 14, 38, 210, 209, 205, 45, 157, 151, 182, 186, 58, 248, 156, 119, 20, 14, 106, 133, 64, 224, 226, 129, 239, 79, 69, 179, 64, 142, 182, 79, 157, 161, 11, 180, 237, 105, 99, 71, 157, 191, 235, 63, 137, 28, 217, 148, 241, 107, 210, 52, 76, 133, 138, 49, 136, 0, 131, 153, 139, 196, 54, 47, 170, 70, 130, 17, 223, 44, 70, 196, 184, 37, 195, 238, 214, 32, 0, 142, 144, 157, 173, 50, 174, 141, 42, 13, 36, 189, 73, 144, 119, 167, 86, 114, 17, 67, 126, 51, 50, 150, 59, 49, 189, 44, 68, 203, 222, 218, 217, 153, 117, 6, 194, 21, 246, 7, 51, 234, 231, 54, 66, 182, 157, 1, 188, 75, 132, 30, 106, 47, 206, 242, 245, 208, 83, 194, 88, 251, 124, 73, 134, 68, 35, 76, 180, 2, 4, 35, 244, 114, 209, 14, 23, 255, 97, 31, 120, 37, 245, 41, 49, 247, 21, 127, 120] }
diff --git a/proptest-regressions/link/double_vlan_slice.txt b/proptest-regressions/link/double_vlan_slice.txt
new file mode 100644
index 0000000..355d3a6
--- /dev/null
+++ b/proptest-regressions/link/double_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 fd2ee3e3872d2ded38124f5a6352bfd29738db63731eab08d738e1a32a036b86 # shrinks to vlan = DoubleVlanHeader { outer: SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x8100 (Customer VLAN Tag (C-TAG) as defined in IEEE Std 802.1Q) }, inner: SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0000 } }, ether_type_non_vlan = 0x0000
diff --git a/proptest-regressions/link/ethernet2_header.txt b/proptest-regressions/link/ethernet2_header.txt
new file mode 100644
index 0000000..dba89f9
--- /dev/null
+++ b/proptest-regressions/link/ethernet2_header.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 e3e72f2a0f7782b605dc416d2a8186c3f04d32f54f9d72069497b71dec1d96d9 # shrinks to input = Ethernet2Header { source: [0, 0, 0, 38, 213, 2], destination: [248, 77, 92, 15, 164, 253], ether_type: 11 }
diff --git a/proptest-regressions/link/ethernet2_slice.txt b/proptest-regressions/link/ethernet2_slice.txt
new file mode 100644
index 0000000..86083c8
--- /dev/null
+++ b/proptest-regressions/link/ethernet2_slice.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 0313dcb8757d7c15ecd8a3af4a0489043408c891a7fb87e6caf643ec7bde8e7d # shrinks to eth = Ethernet2Header { source: [0, 2, 50, 70, 120, 132], destination: [225, 162, 116, 209, 135, 44], ether_type: 0x0001 }
+cc c4709ecdb8cc8ad815bd2e4e5d31d149c4a29c0e9ad2bd5e64164df59c42b871 # shrinks to eth = Ethernet2Header { source: [0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 7, 128, 191], ether_type: 0x0001 }
+cc 51e35db4df6e551682b7047b57e72d8e884a9c61b3f3738c6ec65f0277515337 # shrinks to eth = Ethernet2Header { source: [3, 10, 170, 46, 204, 204], destination: [94, 67, 48, 133, 64, 179], ether_type: 0x0001 }
diff --git a/proptest-regressions/link/single_vlan_header.txt b/proptest-regressions/link/single_vlan_header.txt
new file mode 100644
index 0000000..ef391fe
--- /dev/null
+++ b/proptest-regressions/link/single_vlan_header.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 d15330847f35a69e5da65860dd0c00af1e689f6bb3c5c59c5cc03e5f2f0a2158 # shrinks to input = SingleVlanHeader { priority_code_point: 0, drop_eligible_indicator: false, vlan_identifier: VlanId(0), ether_type: 0x0000 }
diff --git a/proptest-regressions/link/single_vlan_slice.txt b/proptest-regressions/link/single_vlan_slice.txt
new file mode 100644
index 0000000..6a981b9
--- /dev/null
+++ b/proptest-regressions/link/single_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 9fa5be0c50535dff951c67f43c46b17fe04de3d9f044c08c9112f4991fc1dbfb # shrinks to vlan = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0000 }
diff --git a/proptest-regressions/link/vlan_id.txt b/proptest-regressions/link/vlan_id.txt
new file mode 100644
index 0000000..ac0a415
--- /dev/null
+++ b/proptest-regressions/link/vlan_id.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 a28ca5d0468d174393786b905277642e5bd1c6438a8410a79c09c4cc14158f1f # shrinks to valid_value = 0, invalid_value = 256
diff --git a/proptest-regressions/link/vlan_pcp.txt b/proptest-regressions/link/vlan_pcp.txt
new file mode 100644
index 0000000..11f1cd1
--- /dev/null
+++ b/proptest-regressions/link/vlan_pcp.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 c46f85fbef3083dce84c9912bbe5a07c271579fe1016fc80a257a4e621f103ca # shrinks to valid_value = 0, invalid_value = 8
+cc a8c6efb3f79060e61109ba5289b334c5bdd6216b40165b516e15bfa52bcce137 # shrinks to valid_value = 0, invalid_value = 8
diff --git a/proptest-regressions/packet_filters.rs.txt b/proptest-regressions/packet_filters.rs.txt
new file mode 100644
index 0000000..078bda0
--- /dev/null
+++ b/proptest-regressions/packet_filters.rs.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 0fd0278a04b30a10d618763c0c03f610dbeaec3661b8f264590f673be5363846 # shrinks to ref ipv4 = Ipv4Header { ihl: 11, 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: 49, header_checksum: 0, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 238, 161, 238, 54, 33, 252, 165, 192, 37] }, ref ipv6 = Ipv6Header { traffic_class: 233, flow_label: 974616, payload_length: 44475, next_header: 235, hop_limit: 186, source: [145, 233, 192, 110, 139, 99, 37, 92, 244, 136, 22, 106, 192, 236, 204, 158], destination: [217, 167, 1, 43, 51, 177, 255, 124, 199, 126, 68, 131, 107, 116, 242, 151] }
diff --git a/proptest-regressions/sliced_packet.txt b/proptest-regressions/sliced_packet.txt
new file mode 100644
index 0000000..c09057b
--- /dev/null
+++ b/proptest-regressions/sliced_packet.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 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 }
diff --git a/proptest-regressions/transport/icmpv4_header.txt b/proptest-regressions/transport/icmpv4_header.txt
new file mode 100644
index 0000000..4a29dfa
--- /dev/null
+++ b/proptest-regressions/transport/icmpv4_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 bc7b6196699451faa703284f9b393d235597bea7e4f33f227699c1e9e528af37 # shrinks to icmpv4_type = EchoReply(IcmpEchoHeader { id: 0, seq: 0 }), checksum = 0, payload = []
+cc 42e7dbb3e46ac694fc7e996489af2d734b30e753db2fee99cda10dc8e570f4aa # shrinks to icmpv4_type = TimestampRequest(TimestampMessage { id: 0, seq: 0, originate_timestamp: 0, receive_timestamp: 0, transmit_timestamp: 0 }), checksum = 0, payload = []
diff --git a/proptest-regressions/transport/icmpv6_header.txt b/proptest-regressions/transport/icmpv6_header.txt
new file mode 100644
index 0000000..fe79d19
--- /dev/null
+++ b/proptest-regressions/transport/icmpv6_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 ffd752a46c404037c472aa01b1a5c7f98b20118e42015c09b100a9b9b64d8696 # shrinks to ip_header = Ipv6Header { traffic_class: 217, flow_label: Ipv6FlowLabel(981108), payload_length: 16434, next_header: 2 (IGMP - Internet Group Management), hop_limit: 189, source: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 137, 251, 239] }, icmp_type = Unknown { type_u8: 244, code_u8: 110, bytes5to8: [15, 190, 118, 169] }, start_checksum = 18835, bad_len = 5027843371569756216, payload = [98, 191, 45, 21, 55, 95, 238, 218, 192, 43, 50, 110, 99, 18, 104, 210, 39, 176, 84, 62, 205, 10, 202, 59, 83, 156, 164, 3, 99, 232, 248, 212, 137, 55, 198, 189, 93, 54, 126, 216, 216, 112, 234, 255, 79, 53, 65, 252, 183, 175, 168, 92, 8, 21]
+cc 2765acd850616df4c2fce5d30cb2eba2465db3359ca143e22a1c34f37ce9f14a # shrinks to ip_header = Ipv6Header { traffic_class: 127, flow_label: Ipv6FlowLabel(773565), payload_length: 40939, next_header: 8 (EGP - Exterior Gateway Protocol), hop_limit: 223, source: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 65] }, icmp_type = Unknown { type_u8: 181, code_u8: 54, bytes5to8: [152, 109, 154, 4] }, bad_len = 17253463390686651414, payload = [4, 100, 149, 243, 177, 68, 90, 66, 162, 111, 235, 43, 130, 47, 18, 75, 75, 244, 74, 42, 208, 50, 192, 194, 35, 174, 201, 34, 99, 203, 50, 1, 87, 66, 238, 121, 228, 230, 15, 124, 26, 66, 167, 19, 73, 173, 172, 196, 20, 53, 244, 164, 169, 155, 204, 201, 220, 171, 185, 245, 156, 140, 53, 193, 238, 220, 37, 132, 104, 228, 195, 184, 205, 24, 188, 69, 169, 199, 36, 123, 24, 249, 34, 194, 54, 48, 43, 137, 108, 171, 0, 209, 145, 37, 196, 27, 231, 131, 142, 238, 222, 27, 209, 103, 206, 7, 165, 230, 242, 110, 189, 42, 151, 105, 173, 14, 172, 89, 234, 33, 140, 8, 227, 254, 190, 211, 211, 128, 162, 230, 80, 229, 58, 113, 66, 122, 27, 159, 17, 52, 72, 169, 113, 254, 142, 123, 67, 160, 194, 231, 166, 17, 33, 158, 224, 99, 160, 134, 141, 4, 134, 208, 213, 67, 26, 171, 252, 238, 112, 210, 12, 218, 99, 84, 208, 190, 251, 77, 116, 214, 232, 41, 183, 78, 23, 21, 178, 76, 206, 167, 127, 129, 62, 161, 114, 17, 201, 126, 68, 126, 145, 66, 100, 65, 94, 88, 184, 100, 12, 93, 73, 231, 120, 225, 255, 155, 80, 109, 77, 43, 164, 180, 121, 91, 118, 48, 226, 90, 25, 181, 17, 175, 123, 132, 65, 208, 171, 15, 72, 75, 79, 69, 160, 100, 160, 49, 211, 126, 251, 136, 191, 204, 64, 63, 49, 204, 72, 115, 75, 141, 191, 187, 13, 159, 178, 133, 6, 236, 173, 230, 42, 255, 133, 22, 35, 159, 19, 27, 89, 17, 233, 91, 217, 223, 247, 230, 26, 67, 246, 79, 74, 147, 68, 206, 203, 203, 9, 228, 108, 239, 208, 221, 52, 15, 126, 69, 153, 81, 45, 139, 203, 103, 204, 226, 214, 203, 122, 98, 18, 245, 131, 33, 89, 204, 187, 23, 39, 30, 158, 16, 223, 254, 162, 105, 71, 101, 156, 24, 28, 79, 158, 90, 28, 127, 130, 238, 26, 81, 225, 162, 132, 125]
diff --git a/proptest-regressions/transport/tcp_header.txt b/proptest-regressions/transport/tcp_header.txt
new file mode 100644
index 0000000..2d23cd1
--- /dev/null
+++ b/proptest-regressions/transport/tcp_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 31b0fb089633c48a18726ea1da4fe4e9163f6e2921fdc1499a9bba9f37feaf4d # shrinks to header = TcpHeader { source_port: 0, destination_port: 0, sequence_number: 0, acknowledgment_number: 0, data_offset: 6, ns: false, fin: false, syn: false, rst: false, psh: false, ack: false, urg: false, ece: false, cwr: false, window_size: 0, checksum: 11, urgent_pointer: 16871, options: [Err(UnknownId(152))] }
+cc d2a8fca62107cbf941adb1976c9dce7a75c0191dd8c36c55032136867b1ab282 # shrinks to header = TcpHeader { source_port: 0, destination_port: 0, sequence_number: 0, acknowledgment_number: 2, data_offset: 6, ns: false, fin: true, syn: true, rst: false, psh: true, ack: false, urg: true, ece: false, cwr: false, window_size: 36889, checksum: 6852, urgent_pointer: 48998, options: [Err(UnknownId(60))] }
diff --git a/proptest-regressions/transport/tcp_header_slice.txt b/proptest-regressions/transport/tcp_header_slice.txt
new file mode 100644
index 0000000..1853871
--- /dev/null
+++ b/proptest-regressions/transport/tcp_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 e7c41816b38346ff44b21bdc756a29f36f2e1d38d62cabb90566f350949817c4 # shrinks to header = TcpHeader { source_port: 0, destination_port: 0, sequence_number: 394338, acknowledgment_number: 2141047503, ns: false, fin: false, syn: false, rst: false, psh: true, ack: false, urg: false, ece: false, cwr: true, window_size: 30412, checksum: 30779, urgent_pointer: 12128, options: [] }
diff --git a/proptest-regressions/transport/tcp_options.txt b/proptest-regressions/transport/tcp_options.txt
new file mode 100644
index 0000000..c519fe7
--- /dev/null
+++ b/proptest-regressions/transport/tcp_options.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 16837f46966cc3eb7b6c37c8fe755bc83b89533f9489b42983d8f4e2f25ca913 # shrinks to options = []
diff --git a/proptest-regressions/transport/tcp_slice.txt b/proptest-regressions/transport/tcp_slice.txt
new file mode 100644
index 0000000..1c5a286
--- /dev/null
+++ b/proptest-regressions/transport/tcp_slice.txt
@@ -0,0 +1,10 @@
+# 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 3429dff6dbc595ae082d1cc437e3798ffde5f09424b9f1e3b2c496b70a5009f7 # shrinks to tcp = TcpHeader { source_port: 0, destination_port: 0, sequence_number: 0, acknowledgment_number: 0, ns: false, fin: false, syn: false, rst: false, psh: false, ack: false, urg: false, ece: false, cwr: false, window_size: 0, checksum: 0, urgent_pointer: 0, options: [] }
+cc 3a4466de4451685da5af95d84271cf94711e9f349721c2e535c3cfb8dfcce42b # shrinks to tcp = TcpHeader { source_port: 0, destination_port: 0, sequence_number: 0, acknowledgment_number: 0, ns: false, fin: false, syn: false, rst: false, psh: false, ack: false, urg: false, ece: false, cwr: false, window_size: 0, checksum: 0, urgent_pointer: 0, options: [] }
+cc 36e04f7e28d57b5b7c5b56bffe5cfef4108cb48c3406267b9f7ac6e59c9d154d # shrinks to tcp = TcpHeader { source_port: 0, destination_port: 0, sequence_number: 0, acknowledgment_number: 0, ns: false, fin: false, syn: false, rst: false, psh: false, ack: false, urg: false, ece: false, cwr: false, window_size: 0, checksum: 0, urgent_pointer: 0, options: [] }
+cc eab44d2ba159a270c4d6efede60141d39b52c55225c27ee475235df1314c2751 # shrinks to tcp = TcpHeader { source_port: 0, destination_port: 0, sequence_number: 0, acknowledgment_number: 0, ns: false, fin: false, syn: false, rst: false, psh: false, ack: false, urg: false, ece: false, cwr: false, window_size: 0, checksum: 0, urgent_pointer: 0, options: [] }
diff --git a/proptest-regressions/transport/transport_header.txt b/proptest-regressions/transport/transport_header.txt
new file mode 100644
index 0000000..dc7b1a7
--- /dev/null
+++ b/proptest-regressions/transport/transport_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 bea28b0e26af0e201236a15cd393aca1f8a94b70de66dd675cb35283abbba161 # shrinks to ipv4 = Ipv4Header { dscp: Ipv4Dscp(44), ecn: Ipv4Ecn(2), total_len: 35493, identification: 38923, dont_fragment: false, more_fragments: false, fragment_offset: IpFragOffset(4491), time_to_live: 157, protocol: 95 (MICP (deprecated) - Mobile Internetworking Control Pro.), header_checksum: 42512, source: [0, 0, 0, 0], destination: [5, 90, 100, 61], options: [] }, udp = UdpHeader { source_port: 28436, destination_port: 576, length: 42868, checksum: 57008 }, tcp = TcpHeader { source_port: 33227, destination_port: 16188, sequence_number: 2098146184, acknowledgment_number: 963144137, data_offset: 5, ns: true, fin: true, syn: false, rst: false, psh: false, ack: true, urg: false, ece: false, cwr: true, window_size: 17323, checksum: 15303, urgent_pointer: 22752, options: [] }, icmpv4 = Icmpv4Header { icmp_type: Unknown { type_u8: 252, code_u8: 154, bytes5to8: [196, 196, 233, 94] }, checksum: 61125 }, icmpv6 = Icmpv6Header { icmp_type: Unknown { type_u8: 187, code_u8: 225, bytes5to8: [172, 176, 129, 161] }, checksum: 53431 }
+cc a2e9a50ed8bae611d487c399db98a675694df83e5d3d706a84a3c68abc90c303 # shrinks to ipv6 = Ipv6Header { traffic_class: 148, flow_label: Ipv6FlowLabel(494246), payload_length: 8442, next_header: 20 (HMP - Host Monitoring), hop_limit: 228, source: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] }, udp = UdpHeader { source_port: 24006, destination_port: 15797, length: 46422, checksum: 30229 }, tcp = TcpHeader { source_port: 48728, destination_port: 46298, sequence_number: 4167087310, acknowledgment_number: 3560287757, data_offset: 13, ns: true, fin: false, syn: true, rst: true, psh: false, ack: true, urg: false, ece: true, cwr: true, window_size: 28234, checksum: 27234, urgent_pointer: 49127, options: [Err(UnknownId(118))] }, icmpv4 = Icmpv4Header { icmp_type: Unknown { type_u8: 9, code_u8: 191, bytes5to8: [200, 204, 140, 178] }, checksum: 24840 }, icmpv6 = Icmpv6Header { icmp_type: Unknown { type_u8: 35, code_u8: 74, bytes5to8: [138, 73, 230, 137] }, checksum: 63947 }
diff --git a/proptest-regressions/transport/udp_header.txt b/proptest-regressions/transport/udp_header.txt
new file mode 100644
index 0000000..1b0b1b9
--- /dev/null
+++ b/proptest-regressions/transport/udp_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 33a7fb51312ea17d9481182a3c9ac5b79b768564ee007129565e05b1af374cda # shrinks to source_port = 0, destination_port = 0, dummy_checksum = 0, ipv4 = Ipv4Header { dscp: Ipv4Dscp(27), ecn: Ipv4Ecn(2), total_len: 2020, identification: 17171, dont_fragment: false, more_fragments: false, fragment_offset: IpFragOffset(5617), time_to_live: 43, protocol: 223, header_checksum: 55616, source: [199, 143, 81, 185], destination: [178, 183, 108, 161], options: [] }, payload = [50, 40, 103, 182, 130, 191, 69, 120, 36, 125, 61, 174, 10, 18, 139, 72, 223, 100, 31], bad_len = 2705066850224624139
+cc 292410f71c257b6ae77f9b3a4ee37003b2447999e104b371d42bffb4f9cb0edb # shrinks to source_port = 0, destination_port = 0, dummy_checksum = 0, ipv6 = Ipv6Header { traffic_class: 192, flow_label: Ipv6FlowLabel(272844), payload_length: 35736, next_header: 19 (DCN-MEAS - DCN Measurement Subsystems), hop_limit: 24, source: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 1, 104, 215, 211, 202, 179, 244, 153, 59, 51, 20] }, payload = [241, 32, 93, 159, 140, 158, 174, 121, 38, 113, 42, 223, 24], bad_len = 17475589487341371523
diff --git a/proptest-regressions/transport/udp_slice.txt b/proptest-regressions/transport/udp_slice.txt
new file mode 100644
index 0000000..62f3bb7
--- /dev/null
+++ b/proptest-regressions/transport/udp_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 32283d8802a47cec4259af38383590cd01e11af6dce16c620b67ea18f433eceb # shrinks to udp_base = UdpHeader { source_port: 0, destination_port: 0, length: 0, checksum: 0 }
diff --git a/src/checksum.rs b/src/checksum.rs
new file mode 100644
index 0000000..d9e0551
--- /dev/null
+++ b/src/checksum.rs
@@ -0,0 +1,925 @@
+/// Helper for calculating the sum of all 16 bit words checksums used in
+/// in checksum fields in TCP and UDP headers.
+#[derive(Clone, Debug, Default, Eq, PartialEq)]
+pub struct Sum16BitWords {
+    /// Partial sum
+    #[cfg(target_pointer_width = "64")]
+    sum: u64,
+
+    /// Partial sum
+    #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
+    sum: u32,
+}
+
+impl Sum16BitWords {
+    pub fn new() -> Sum16BitWords {
+        Sum16BitWords { sum: 0 }
+    }
+
+    /// Add the given slice to the checksum. In case the slice
+    /// has a length that is not multiple of 2 the last byte
+    /// will be padded with 0.
+    #[inline]
+    #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
+    pub fn add_slice(self, slice: &[u8]) -> Sum16BitWords {
+        Sum16BitWords {
+            sum: u32_16bit_word::add_slice(self.sum, slice),
+        }
+    }
+
+    /// Add the given slice to the checksum. In case the slice
+    /// has a length that is not multiple of 2 the last byte
+    /// will be padded with 0.
+    #[inline]
+    #[cfg(target_pointer_width = "64")]
+    pub fn add_slice(self, slice: &[u8]) -> Sum16BitWords {
+        Sum16BitWords {
+            sum: u64_16bit_word::add_slice(self.sum, slice),
+        }
+    }
+
+    /// Add a 2 byte word.
+    #[inline]
+    #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
+    pub fn add_2bytes(self, value: [u8; 2]) -> Sum16BitWords {
+        Sum16BitWords {
+            sum: u32_16bit_word::add_2bytes(self.sum, value),
+        }
+    }
+
+    /// Add a 2 byte word.
+    #[inline]
+    #[cfg(target_pointer_width = "64")]
+    pub fn add_2bytes(self, value: [u8; 2]) -> Sum16BitWords {
+        Sum16BitWords {
+            sum: u64_16bit_word::add_2bytes(self.sum, value),
+        }
+    }
+
+    /// Add a 4 byte word.
+    #[inline]
+    #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
+    pub fn add_4bytes(&mut self, value: [u8; 4]) -> Sum16BitWords {
+        Sum16BitWords {
+            sum: u32_16bit_word::add_4bytes(self.sum, value),
+        }
+    }
+
+    /// Add a 4 byte word.
+    #[inline]
+    #[cfg(target_pointer_width = "64")]
+    pub fn add_4bytes(&mut self, value: [u8; 4]) -> Sum16BitWords {
+        Sum16BitWords {
+            sum: u64_16bit_word::add_4bytes(self.sum, value),
+        }
+    }
+
+    /// Add a 8 byte word.
+    #[inline]
+    #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
+    pub fn add_8bytes(&mut self, value: [u8; 8]) -> Sum16BitWords {
+        self.add_4bytes([value[0], value[1], value[2], value[3]])
+            .add_4bytes([value[4], value[5], value[6], value[7]])
+    }
+
+    /// Add a 8 byte word.
+    #[inline]
+    #[cfg(target_pointer_width = "64")]
+    pub fn add_8bytes(&mut self, value: [u8; 8]) -> Sum16BitWords {
+        Sum16BitWords {
+            sum: u64_16bit_word::add_8bytes(self.sum, value),
+        }
+    }
+
+    /// Add a 16 bytes.
+    #[inline]
+    pub fn add_16bytes(&mut self, value: [u8; 16]) -> Sum16BitWords {
+        self.add_8bytes([
+            value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7],
+        ])
+        .add_8bytes([
+            value[8], value[9], value[10], value[11], value[12], value[13], value[14], value[15],
+        ])
+    }
+
+    /// Converts summed up words from an u32 to an u16 ones complement
+    /// which can be used in a ipv4 checksum.
+    #[inline]
+    #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
+    pub fn ones_complement(&self) -> u16 {
+        u32_16bit_word::ones_complement(self.sum)
+    }
+
+    /// Converts summed up words from an u32 to an u16 ones complement
+    /// which can be used in a ipv4 checksum.
+    #[inline]
+    #[cfg(target_pointer_width = "64")]
+    pub fn ones_complement(&self) -> u16 {
+        u64_16bit_word::ones_complement(self.sum)
+    }
+
+    /// Converts summed up words from an u32 to an u16 ones complement
+    /// with 0 being replaced by 0xffff (useful for TCP and UDP).
+    ///
+    /// This kind of checksum is used in TCP and UDP headers.
+    #[inline]
+    #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
+    pub fn to_ones_complement_with_no_zero(&self) -> u16 {
+        u32_16bit_word::ones_complement_with_no_zero(self.sum)
+    }
+
+    /// Converts summed up words from an u32 to an u16 ones complement
+    /// with 0 being replaced by 0xffff (useful for TCP and UDP).
+    ///
+    /// This kind of checksum is used in TCP and UDP headers.
+    #[inline]
+    #[cfg(target_pointer_width = "64")]
+    pub fn to_ones_complement_with_no_zero(&self) -> u16 {
+        u64_16bit_word::ones_complement_with_no_zero(self.sum)
+    }
+}
+
+#[cfg(test)]
+mod sum16_bit_words_tests {
+    use super::*;
+    use alloc::format;
+
+    #[test]
+    fn new() {
+        assert_eq!(0xffff, Sum16BitWords::new().ones_complement());
+    }
+
+    #[test]
+    fn add_slice() {
+        assert_eq!(
+            !u16::from_ne_bytes([0x12, 0x34]),
+            Sum16BitWords::new()
+                .add_slice(&[0x12, 0x34])
+                .ones_complement()
+        );
+    }
+
+    #[test]
+    fn add_2bytes() {
+        assert_eq!(
+            !u16::from_ne_bytes([0xf0, 0x0f]),
+            Sum16BitWords::new()
+                .add_2bytes([0xf0, 0x0f])
+                .ones_complement()
+        );
+    }
+
+    #[test]
+    fn add_4bytes() {
+        assert_eq!(
+            !(u16::from_ne_bytes([0x12, 0x34]) + u16::from_ne_bytes([0x56, 0x78])),
+            Sum16BitWords::new()
+                .add_4bytes([0x12, 0x34, 0x56, 0x78])
+                .ones_complement()
+        );
+    }
+
+    #[test]
+    fn add_8bytes() {
+        assert_eq!(
+            !(u16::from_ne_bytes([0x12, 0x34])
+                + u16::from_ne_bytes([0x56, 0x78])
+                + u16::from_ne_bytes([0x23, 0x22])
+                + u16::from_ne_bytes([0x34, 0x11])),
+            Sum16BitWords::new()
+                .add_8bytes([0x12, 0x34, 0x56, 0x78, 0x23, 0x22, 0x34, 0x11])
+                .ones_complement()
+        );
+    }
+
+    #[test]
+    fn add_16bytes() {
+        assert_eq!(
+            u32_16bit_word::ones_complement(u32_16bit_word::add_4bytes(
+                u32_16bit_word::add_4bytes(
+                    u32_16bit_word::add_4bytes(
+                        u32_16bit_word::add_4bytes(0, [0x12, 0x34, 0x56, 0x78]),
+                        [0x9a, 0xbc, 0xde, 0xf0]
+                    ),
+                    [0x0f, 0xed, 0xcb, 0xa9]
+                ),
+                [0x87, 0x65, 0x43, 0x21]
+            )),
+            Sum16BitWords::new()
+                .add_16bytes([
+                    0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87,
+                    0x65, 0x43, 0x21,
+                ])
+                .ones_complement()
+        );
+    }
+
+    #[test]
+    fn ones_complement() {
+        assert_eq!(
+            !u16::from_ne_bytes([0xf0, 0x0f]),
+            Sum16BitWords::new()
+                .add_2bytes([0xf0, 0x0f])
+                .ones_complement()
+        );
+    }
+
+    #[test]
+    fn to_ones_complement_with_no_zero() {
+        // normal case
+        assert_eq!(
+            !u16::from_ne_bytes([0xf0, 0x0f]),
+            Sum16BitWords::new()
+                .add_2bytes([0xf0, 0x0f])
+                .to_ones_complement_with_no_zero()
+        );
+
+        // zero case
+        assert_eq!(
+            0xffffu16,
+            Sum16BitWords::new()
+                // ones complement would result in 0
+                // will be converted to 0xffff as 0
+                // is a reserved value
+                .add_2bytes([0xff, 0xff])
+                .to_ones_complement_with_no_zero()
+        );
+    }
+
+    #[test]
+    fn debug() {
+        let input = Sum16BitWords::new();
+        assert_eq!(
+            &format!("Sum16BitWords {{ sum: {} }}", input.sum),
+            &format!("{:?}", input)
+        );
+    }
+
+    #[test]
+    fn default() {
+        let d: Sum16BitWords = Default::default();
+        assert_eq!(d.sum, 0);
+    }
+
+    #[test]
+    fn clone_eq() {
+        let value = Sum16BitWords::new();
+        assert_eq!(value.clone(), value)
+    }
+}
+
+/// Helper functions for calculating a 16 bit checksum using
+/// a u32 to sum up all values.
+pub mod u32_16bit_word {
+
+    /// Add a 4 byte word.
+    #[inline]
+    pub fn add_4bytes(start: u32, value: [u8; 4]) -> u32 {
+        let (sum, carry) = start.overflowing_add(u32::from_ne_bytes(value));
+        sum + (carry as u32)
+    }
+
+    /// Add a 2 byte word.
+    #[inline]
+    pub fn add_2bytes(start: u32, value: [u8; 2]) -> u32 {
+        let (sum, carry) = start.overflowing_add(u32::from(u16::from_ne_bytes(value)));
+        sum + (carry as u32)
+    }
+
+    /// Add the given slice to the checksum. In case the slice
+    /// has a length that is not multiple of 2 the last byte
+    /// will be padded with 0.
+    #[inline]
+    pub fn add_slice(start_sum: u32, slice: &[u8]) -> u32 {
+        let mut sum: u32 = start_sum;
+
+        // sum up all 4 byte values
+        let end_32 = slice.len() - (slice.len() % 4);
+        for i in (0..end_32).step_by(4) {
+            sum = add_4bytes(
+                sum,
+                // SAFETY:
+                // Guranteed to always have at least 4 bytes to read
+                // from i. As end_32 is gurenateed to be a multiple of
+                // 4 bytes with a size equal or less then slice.len().
+                unsafe {
+                    [
+                        *slice.get_unchecked(i),
+                        *slice.get_unchecked(i + 1),
+                        *slice.get_unchecked(i + 2),
+                        *slice.get_unchecked(i + 3),
+                    ]
+                },
+            );
+        }
+
+        // in case 2 bytes are left add them as an word
+        if slice.len() - end_32 >= 2 {
+            sum = add_2bytes(
+                sum,
+                // SAFETY:
+                // If check guarantees there to be at least
+                // 2 bytes.
+                unsafe {
+                    [
+                        *slice.get_unchecked(end_32),
+                        *slice.get_unchecked(end_32 + 1),
+                    ]
+                },
+            );
+        }
+
+        // unaligned end pad the last byte with
+        if 0 != slice.len() % 2 {
+            sum = add_2bytes(
+                sum,
+                // SAFETY:
+                // If check guarantees there to be at least
+                // 2 bytes.
+                unsafe { [*slice.get_unchecked(slice.len() - 1), 0] },
+            );
+        }
+
+        // done
+        sum
+    }
+
+    /// Converts summed up words from an u32 to an u16 with 0 being replaced by 0xffff (useful
+    /// for TCP and UDP headers).
+    ///
+    /// This kind of checksum is used in TCP and udp headers.
+    #[inline]
+    pub fn ones_complement_with_no_zero(sum: u32) -> u16 {
+        // In case of 0 use the ones complement (zero is reserved
+        // value for no checksum).
+        let u16value = ones_complement(sum);
+        if u16value == 0 {
+            0xffff
+        } else {
+            u16value
+        }
+    }
+
+    /// Converts summed up words from an u32 to an u16 which can be used in a ipv4.
+    #[inline]
+    pub fn ones_complement(sum: u32) -> u16 {
+        // Add the upper 16 bits to the lower 16 bits twice.
+        //
+        // Notes: Two carry adds are needed as the first one could
+        //        result in an additional carry add.
+        let first = ((sum >> 16) & 0xffff) + (sum & 0xffff);
+        let u16value = (((first >> 16) & 0xffff) + (first & 0xffff)) as u16;
+
+        // switch back to big endian (allows to use
+        // native endinaess during calculations).
+        !u16value
+    }
+
+    #[cfg(test)]
+    mod tests {
+        use super::*;
+
+        #[test]
+        fn add_4bytes_test() {
+            // trivial case
+            assert_eq!(0, add_4bytes(0, [0, 0, 0, 0]));
+            // check that the carry gets added
+            assert_eq!(
+                0xffff_ffff, // normal overflow would result in 0xffff_fffe
+                add_4bytes(0xffff_ffff, [0xff, 0xff, 0xff, 0xff])
+            );
+            // non max & min values
+            assert_eq!(
+                0x1234_5678 + u32::from_ne_bytes([0x23, 0x45, 0x67, 0x89]),
+                add_4bytes(0x1234_5678, [0x23, 0x45, 0x67, 0x89])
+            );
+        }
+
+        #[test]
+        fn add_2bytes_test() {
+            // trivial case
+            assert_eq!(0, add_2bytes(0, [0, 0]));
+            // check that the carry gets added
+            assert_eq!(
+                0x0000_ffff, // normal overflow would result in 0x10000fffe
+                add_2bytes(0xffff_ffff, [0xff, 0xff])
+            );
+            // non max & min values
+            assert_eq!(
+                0x1234_5678 + u32::from(u16::from_ne_bytes([0x23, 0x45])),
+                add_2bytes(0x1234_5678, [0x23, 0x45])
+            );
+        }
+
+        #[test]
+        fn add_slice_test() {
+            // empty
+            assert_eq!(0x1234, add_slice(0x1234, &[]));
+
+            // aligned
+            assert_eq!(
+                0x1 + u32::from_ne_bytes([0x11, 0x12, 0x13, 0x14])
+                    + u32::from_ne_bytes([0x15, 0x16, 0x17, 0x18])
+                    + u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])
+                    + u32::from_ne_bytes([0x1d, 0x1e, 0x1f, 0x10]),
+                add_slice(
+                    0x1,
+                    &[
+                        0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
+                        0x1d, 0x1e, 0x1f, 0x10,
+                    ]
+                )
+            );
+
+            // aligned with carry
+            assert_eq!(
+                0x1 +
+                0x3 + // expected carry
+                u32::from_ne_bytes([0xf1, 0x11, 0x10, 0xf0]).wrapping_add(
+                    u32::from_ne_bytes([0xf2, 0x12, 0x11, 0xf1]).wrapping_add(
+                        u32::from_ne_bytes([0xf3, 0x13, 0x12, 0xf2]).wrapping_add(
+                            u32::from_ne_bytes([0xf4, 0x14, 0x13, 0xf3])
+                        )
+                    )
+                ),
+                add_slice(
+                    0x1,
+                    &[
+                        0xf1, 0x11, 0x10, 0xf0, 0xf2, 0x12, 0x11, 0xf1, 0xf3, 0x13, 0x12, 0xf2,
+                        0xf4, 0x14, 0x13, 0xf3,
+                    ]
+                )
+            );
+
+            // 1 byte unalgined
+            assert_eq!(
+                0x1 + u32::from_ne_bytes([0x11, 0x12, 0x13, 0x14])
+                    + u32::from_ne_bytes([0x15, 0x16, 0x17, 0x18])
+                    + u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])
+                    + u32::from(u16::from_ne_bytes([0x1d, 0x1e]))
+                    + u32::from(u16::from_ne_bytes([0x1f, 0x00])),
+                add_slice(
+                    0x1,
+                    &[
+                        0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
+                        0x1d, 0x1e, 0x1f,
+                    ]
+                )
+            );
+
+            // 2 byte unaligned
+            assert_eq!(
+                0x1 + u32::from_ne_bytes([0x11, 0x12, 0x13, 0x14])
+                    + u32::from_ne_bytes([0x15, 0x16, 0x17, 0x18])
+                    + u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])
+                    + u32::from(u16::from_ne_bytes([0x1d, 0x1e])),
+                add_slice(
+                    0x1,
+                    &[
+                        0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
+                        0x1d, 0x1e,
+                    ]
+                )
+            );
+
+            // 4 byte unaligned
+            assert_eq!(
+                0x1 + u32::from_ne_bytes([0x11, 0x12, 0x13, 0x14])
+                    + u32::from_ne_bytes([0x15, 0x16, 0x17, 0x18])
+                    + u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])
+                    + u32::from(u16::from_ne_bytes([0x1d, 0x00])),
+                add_slice(
+                    0x1,
+                    &[
+                        0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
+                        0x1d,
+                    ]
+                )
+            );
+        }
+
+        #[test]
+        fn ones_complement_with_no_zero_test() {
+            // zero case
+            assert_eq!(0xffff, ones_complement_with_no_zero(0));
+
+            // 0xffff should stay 0xffff (0 is reserved for no checksum)
+            assert_eq!(0xffff, ones_complement_with_no_zero(0xffff));
+
+            // big endian conversion check
+            assert_eq!(!0x1234u16, ones_complement_with_no_zero(0x1234),);
+
+            // add of the upper and lower 16 bits without a carry
+            assert_eq!(
+                !(0x2345u16 + 0x1234),
+                ones_complement_with_no_zero(0x2345_1234),
+            );
+
+            // add which in itself will again produce a carry
+            assert_eq!(
+                !(((0x1456u32 + 0xf123u32 + 1u32) & 0xffff) as u16),
+                ones_complement_with_no_zero(0x1456_f123),
+            );
+        }
+
+        #[test]
+        fn ones_complement_test() {
+            // zero case
+            assert_eq!(0xffff, ones_complement(0));
+
+            // check that zero is not reserved
+            assert_eq!(0, ones_complement(0xffff));
+
+            // big endian conversion check
+            assert_eq!(!0x1234u16, ones_complement(0x1234),);
+
+            // add of the upper and lower 16 bits without a carry
+            assert_eq!(!(0x2345u16 + 0x1234u16), ones_complement(0x2345_1234),);
+
+            // add which in itself will again produce a carry
+            assert_eq!(
+                !(((0x1456u32 + 0xf123u32 + 1u32) & 0xffff) as u16),
+                ones_complement(0x1456_f123),
+            );
+        }
+    }
+}
+
+/// Helper functions for calculating a 16 bit checksum using
+/// a u64 to sum up all values.
+pub mod u64_16bit_word {
+
+    /// Add a 8 byte word.
+    #[inline]
+    pub fn add_8bytes(start: u64, value: [u8; 8]) -> u64 {
+        let (sum, carry) = start.overflowing_add(u64::from_ne_bytes(value));
+        sum + (carry as u64)
+    }
+
+    /// Add a 4 byte word.
+    #[inline]
+    pub fn add_4bytes(start: u64, value: [u8; 4]) -> u64 {
+        let (sum, carry) = start.overflowing_add(u64::from(u32::from_ne_bytes(value)));
+        sum + (carry as u64)
+    }
+
+    /// Add a 2 byte word.
+    #[inline]
+    pub fn add_2bytes(start: u64, value: [u8; 2]) -> u64 {
+        let (sum, carry) = start.overflowing_add(u64::from(u16::from_ne_bytes(value)));
+        sum + (carry as u64)
+    }
+
+    /// Add the given slice to the checksum. In case the slice
+    /// has a length that is not multiple of 2 the last byte
+    /// will be padded with 0.
+    #[inline]
+    pub fn add_slice(start_sum: u64, slice: &[u8]) -> u64 {
+        let mut sum: u64 = start_sum;
+
+        // sum up all 4 byte values
+        let end_64 = slice.len() - (slice.len() % 8);
+        for i in (0..end_64).step_by(8) {
+            sum = add_8bytes(
+                sum,
+                // SAFETY:
+                // Guranteed to always have at least 8 bytes to read
+                // from i. As end_64 is gurenateed to be a multiple of
+                // 8 bytes with a size equal or less then slice.len().
+                unsafe {
+                    [
+                        *slice.get_unchecked(i),
+                        *slice.get_unchecked(i + 1),
+                        *slice.get_unchecked(i + 2),
+                        *slice.get_unchecked(i + 3),
+                        *slice.get_unchecked(i + 4),
+                        *slice.get_unchecked(i + 5),
+                        *slice.get_unchecked(i + 6),
+                        *slice.get_unchecked(i + 7),
+                    ]
+                },
+            );
+        }
+
+        // in case 4 or more bytes are left add the first 4 bytes
+        let end_32 = if slice.len() - end_64 >= 4 {
+            sum = add_4bytes(
+                sum,
+                // SAFETY:
+                // If check guarantees there to be at least
+                // 2 bytes.
+                unsafe {
+                    [
+                        *slice.get_unchecked(end_64),
+                        *slice.get_unchecked(end_64 + 1),
+                        *slice.get_unchecked(end_64 + 2),
+                        *slice.get_unchecked(end_64 + 3),
+                    ]
+                },
+            );
+
+            // shift by 4
+            end_64 + 4
+        } else {
+            end_64
+        };
+
+        // in case 2 bytes are left add them as an word
+        if slice.len() - end_32 >= 2 {
+            sum = add_2bytes(
+                sum,
+                // SAFETY:
+                // If check guarantees there to be at least
+                // 2 bytes.
+                unsafe {
+                    [
+                        *slice.get_unchecked(end_32),
+                        *slice.get_unchecked(end_32 + 1),
+                    ]
+                },
+            );
+        }
+
+        // unaligned end pad the last byte with
+        if 0 != slice.len() % 2 {
+            sum = add_2bytes(
+                sum,
+                // SAFETY:
+                // If check guarantees there to be at least
+                // 2 bytes.
+                unsafe { [*slice.get_unchecked(slice.len() - 1), 0] },
+            );
+        }
+
+        // done
+        sum
+    }
+
+    /// Converts summed up words from an u64 to an u16 with 0 being replaced by 0xffff (useful
+    /// for TCP and UDP headers).
+    ///
+    /// This kind of checksum is used in TCP and udp headers.
+    #[inline]
+    pub fn ones_complement_with_no_zero(sum: u64) -> u16 {
+        // In case of 0 use the ones complement (zero is reserved
+        // value for no checksum).
+        let u16value = ones_complement(sum);
+        if u16value == 0 {
+            0xffff
+        } else {
+            u16value
+        }
+    }
+
+    /// Converts summed up words from an u64 to an u16 which can be used in a ipv4.
+    #[inline]
+    pub fn ones_complement(sum: u64) -> u16 {
+        let first = ((sum >> 48) & 0xffff)
+            + ((sum >> 32) & 0xffff)
+            + ((sum >> 16) & 0xffff)
+            + (sum & 0xffff);
+        // Add the upper 16 bits to the lower 16 bits twice.
+        //
+        // Notes: Two carry adds are needed as the first one could
+        //        result in an additional carry add.
+        let second = ((first >> 16) & 0xffff) + (first & 0xffff);
+        let u16value = (((second >> 16) & 0xffff) + (second & 0xffff)) as u16;
+
+        // switch back to big endian (allows to use
+        // native endinaess during calculations).
+        !u16value
+    }
+
+    #[cfg(test)]
+    mod tests {
+        use super::*;
+        use proptest::prelude::*;
+
+        #[test]
+        fn add_8bytes_test() {
+            // trivial case
+            assert_eq!(0, add_8bytes(0, [0, 0, 0, 0, 0, 0, 0, 0]));
+            // check that the carry gets added
+            assert_eq!(
+                0xffff_ffff_ffff_ffff, // normal overflow would result in 0xffff_ffff_ffff_fffe
+                add_8bytes(
+                    0xffff_ffff_ffff_ffff,
+                    [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
+                )
+            );
+            // non max & min values
+            assert_eq!(
+                0x1234_5678_1234_5678
+                    + u64::from_ne_bytes([0x23, 0x45, 0x67, 0x89, 0x11, 0x22, 0x33, 0x44]),
+                add_8bytes(
+                    0x1234_5678_1234_5678,
+                    [0x23, 0x45, 0x67, 0x89, 0x11, 0x22, 0x33, 0x44]
+                )
+            );
+        }
+
+        #[test]
+        fn add_4bytes_test() {
+            // trivial case
+            assert_eq!(0, add_4bytes(0, [0, 0, 0, 0]));
+            // check that the carry gets added
+            assert_eq!(
+                0xffff_ffff, // normal overflow would result in 0xffff_fffe
+                add_4bytes(0xffff_ffff_ffff_ffff, [0xff, 0xff, 0xff, 0xff])
+            );
+            // non max & min values
+            assert_eq!(
+                0x1234_5678_1234_5678 + u64::from(u32::from_ne_bytes([0x23, 0x45, 0x67, 0x89])),
+                add_4bytes(0x1234_5678_1234_5678, [0x23, 0x45, 0x67, 0x89])
+            );
+        }
+
+        #[test]
+        fn add_2bytes_test() {
+            // trivial case
+            assert_eq!(0, add_2bytes(0, [0, 0]));
+            // check that the carry gets added
+            assert_eq!(
+                0xffff, // normal overflow would result in 0xfffe
+                add_2bytes(0xffff_ffff_ffff_ffff, [0xff, 0xff])
+            );
+            // non max & min values
+            assert_eq!(
+                0x9876_0123_1234_5678 + u64::from(u16::from_ne_bytes([0x23, 0x45])),
+                add_2bytes(0x9876_0123_1234_5678, [0x23, 0x45])
+            );
+        }
+
+        #[test]
+        fn add_slice_test() {
+            // empty
+            assert_eq!(0x1234, add_slice(0x1234, &[]));
+
+            // aligned
+            assert_eq!(
+                0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18])
+                    + u64::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10]),
+                add_slice(
+                    0x1,
+                    &[
+                        0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
+                        0x1d, 0x1e, 0x1f, 0x10,
+                    ]
+                )
+            );
+
+            // aligned with carry
+            assert_eq!(
+                0x1 +
+                0x1 + // expected carry
+                u64::from_ne_bytes([0xf1, 0x11, 0x10, 0xf0, 0xf2, 0x12, 0x11, 0xf1]).wrapping_add(
+                    u64::from_ne_bytes([0xf3, 0x13, 0x12, 0xf2, 0xf4, 0x14, 0x13, 0xf3])
+                ),
+                add_slice(
+                    0x1,
+                    &[
+                        0xf1, 0x11, 0x10, 0xf0, 0xf2, 0x12, 0x11, 0xf1, 0xf3, 0x13, 0x12, 0xf2,
+                        0xf4, 0x14, 0x13, 0xf3,
+                    ]
+                )
+            );
+
+            // unaligned access
+            {
+                let base_data = [
+                    0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+                    0x1e, 0x1f, 0x00,
+                ];
+
+                // 1 byte unaligned
+                assert_eq!(
+                    0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18])
+                        + u64::from(u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c]))
+                        + u64::from(u16::from_ne_bytes([0x1d, 0x1e]))
+                        + u64::from(u16::from_ne_bytes([0x1f, 0x00])),
+                    add_slice(0x1, &base_data[..base_data.len() - 1])
+                );
+
+                // 2 byte unaligned
+                assert_eq!(
+                    0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18])
+                        + u64::from(u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c]))
+                        + u64::from(u16::from_ne_bytes([0x1d, 0x1e])),
+                    add_slice(0x1, &base_data[..base_data.len() - 2])
+                );
+
+                // 3 byte unaligned
+                assert_eq!(
+                    0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18])
+                        + u64::from(u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c]))
+                        + u64::from(u16::from_ne_bytes([0x1d, 0x00])),
+                    add_slice(0x1, &base_data[..base_data.len() - 3])
+                );
+
+                // 4 byte unaligned
+                assert_eq!(
+                    0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18])
+                        + u64::from(u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])),
+                    add_slice(0x1, &base_data[..base_data.len() - 4])
+                );
+
+                // 5 byte unaligned
+                assert_eq!(
+                    0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18])
+                        + u64::from(u16::from_ne_bytes([0x19, 0x1a]))
+                        + u64::from(u16::from_ne_bytes([0x1b, 0x00])),
+                    add_slice(0x1, &base_data[..base_data.len() - 5])
+                );
+
+                // 6 byte unaligned
+                assert_eq!(
+                    0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18])
+                        + u64::from(u16::from_ne_bytes([0x19, 0x1a])),
+                    add_slice(0x1, &base_data[..base_data.len() - 6])
+                );
+
+                // 6 byte unaligned
+                assert_eq!(
+                    0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18])
+                        + u64::from(u16::from_ne_bytes([0x19, 0x00])),
+                    add_slice(0x1, &base_data[..base_data.len() - 7])
+                );
+            }
+        }
+
+        #[test]
+        fn ones_complement_with_no_zero_test() {
+            // zero case
+            assert_eq!(0xffff, ones_complement_with_no_zero(0));
+
+            // 0xffff should stay 0xffff (0 is reserved for no checksum)
+            assert_eq!(0xffff, ones_complement_with_no_zero(0xffff));
+
+            // big endian conversion check
+            assert_eq!(!0x1234u16, ones_complement_with_no_zero(0x1234),);
+
+            // add of the upper and lower 16 bits without a carry
+            assert_eq!(
+                !(0x2456u16 + 0x1345 + 0x2345u16 + 0x1234u16),
+                ones_complement_with_no_zero(0x2456_1345_2345_1234),
+            );
+
+            // add which in itself will again produce two as carry
+            assert_eq!(
+                !(((0x1234 + 0xf234u32 + 0x1456u32 + 0xf123u32 + 2u32) & 0xffff) as u16),
+                ones_complement_with_no_zero(0x1234_f234_1456_f123),
+            );
+        }
+
+        #[test]
+        fn ones_complement_test() {
+            // zero case
+            assert_eq!(0xffff, ones_complement(0));
+
+            // check that zero is not reserved
+            assert_eq!(0, ones_complement(0xffff));
+
+            // big endian conversion check
+            assert_eq!(!0x1234u16, ones_complement(0x1234),);
+
+            // add of the upper and lower 16 bits without a carry
+            assert_eq!(
+                !(0x2456u16 + 0x1345 + 0x2345u16 + 0x1234u16),
+                ones_complement(0x2456_1345_2345_1234),
+            );
+
+            // add which in itself will again produce two as carry
+            assert_eq!(
+                !(((0x1234 + 0xf234u32 + 0x1456u32 + 0xf123u32 + 2u32) & 0xffff) as u16),
+                ones_complement(0x1234_f234_1456_f123),
+            );
+
+            // will result in a first 16bit sum that will have to be
+            // carry added twice
+            assert_eq!(!1, ones_complement(0x02f6_e312_7fd7_9a20),);
+        }
+
+        proptest! {
+            #[test]
+            #[cfg_attr(miri, ignore)] // vec allocation reduces miri runspeed too much
+            fn u32_u16_comparison(
+                data in proptest::collection::vec(any::<u8>(), 0..0xfffusize)
+            ) {
+                use crate::checksum::*;
+
+                let u32_oc = u32_16bit_word::ones_complement(
+                    u32_16bit_word::add_slice(0, &data)
+                );
+                let u64_oc = u64_16bit_word::ones_complement(
+                    u64_16bit_word::add_slice(0, &data)
+                );
+                assert_eq!(u32_oc, u64_oc);
+
+                let struct_oc = Sum16BitWords::new()
+                    .add_slice(&data)
+                    .ones_complement();
+                assert_eq!(u32_oc, struct_oc);
+            }
+        }
+    }
+}
diff --git a/src/compositions_tests.rs b/src/compositions_tests.rs
new file mode 100644
index 0000000..fd04b69
--- /dev/null
+++ b/src/compositions_tests.rs
@@ -0,0 +1,662 @@
+use super::*;
+
+use crate::test_gens::*;
+use alloc::{vec, vec::Vec};
+use proptest::prelude::*;
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+struct ComponentTest {
+    link: Option<LinkHeader>,
+    vlan: Option<VlanHeader>,
+    ip: Option<IpHeaders>,
+    transport: Option<TransportHeader>,
+    payload: Vec<u8>,
+}
+
+static VLAN_ETHER_TYPES: &'static [EtherType] = &[
+    EtherType::VLAN_TAGGED_FRAME,
+    EtherType::PROVIDER_BRIDGING,
+    EtherType::VLAN_DOUBLE_TAGGED_FRAME,
+];
+
+impl ComponentTest {
+    fn serialize(&self) -> Vec<u8> {
+        let mut buffer = Vec::<u8>::with_capacity(
+            match &self.link {
+                Some(header) => header.header_len(),
+                None => 0,
+            } + match &self.vlan {
+                Some(header) => header.header_len(),
+                None => 0,
+            } + match &self.ip {
+                Some(headers) => headers.header_len(),
+                None => 0,
+            } + match &self.transport {
+                Some(header) => header.header_len(),
+                None => 0,
+            } + self.payload.len(),
+        );
+
+        //fill all the elements
+        match &self.link {
+            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 => {}
+        }
+        match &self.ip {
+            Some(IpHeaders::Ipv4(header, exts)) => {
+                header.write_raw(&mut buffer).unwrap();
+                exts.write(&mut buffer, header.protocol).unwrap();
+            }
+            Some(IpHeaders::Ipv6(header, exts)) => {
+                header.write(&mut buffer).unwrap();
+                exts.write(&mut buffer, header.next_header).unwrap();
+            }
+            None => {}
+        }
+        match &self.transport {
+            Some(TransportHeader::Icmpv6(header)) => header.write(&mut buffer).unwrap(),
+            Some(TransportHeader::Icmpv4(header)) => header.write(&mut buffer).unwrap(),
+            Some(TransportHeader::Udp(header)) => header.write(&mut buffer).unwrap(),
+            Some(TransportHeader::Tcp(header)) => header.write(&mut buffer).unwrap(),
+            None => {}
+        }
+        use std::io::Write;
+        buffer.write(&self.payload[..]).unwrap();
+        buffer
+    }
+
+    /// Serialize the headers & payload specified in the headers and check that
+    /// the different decoding & slicing methods for entire packets work correctly.
+    ///
+    /// The following functions will be checked if they work correctly:
+    /// * `SlicedPacket::from_ethernet`
+    /// * `SlicedPacket::from_ip`
+    /// * `PacketHeaders::from_ethernet_slice`
+    /// * `PacketHeaders::from_ip_slice`
+    fn run(&self) {
+        // clone the test so the length fields can be adapted
+        let mut test = self.clone();
+
+        // set the payload length
+        if let Some(ip) = test.ip.as_mut() {
+            match ip {
+                IpHeaders::Ipv4(ipv4, exts) => {
+                    ipv4.set_payload_len(
+                        exts.header_len()
+                            + self.transport.as_ref().map_or(0, |t| t.header_len())
+                            + self.payload.len(),
+                    )
+                    .unwrap();
+                }
+                IpHeaders::Ipv6(ipv6, exts) => {
+                    ipv6.set_payload_length(
+                        exts.header_len()
+                            + self.transport.as_ref().map_or(0, |t| t.header_len())
+                            + self.payload.len(),
+                    )
+                    .unwrap();
+                }
+            }
+        }
+        if let Some(TransportHeader::Udp(udp)) = test.transport.as_mut() {
+            udp.length = udp.header_len_u16() + self.payload.len() as u16;
+        }
+
+        //packet with ethernet2 & vlan headers
+        {
+            //serialize to buffer
+            let buffer = test.serialize();
+
+            // PacketHeaders::from_ethernet_slice
+            test.assert_headers(PacketHeaders::from_ethernet_slice(&buffer).unwrap());
+
+            // SlicedPacket::from_ethernet
+            test.assert_sliced_packet(SlicedPacket::from_ethernet(&buffer).unwrap());
+
+            // create unexpected end of slice errors for the different headers
+            for len in test.invalid_ser_lengths() {
+                if let Some(len) = len {
+                    assert!(PacketHeaders::from_ethernet_slice(&buffer[..len]).is_err());
+                    assert!(SlicedPacket::from_ethernet(&buffer[..len]).is_err());
+                }
+            }
+        }
+
+        // packet data starting right after the link layer (tests from_ether_type functions)
+        {
+            // remove the link layer
+            let ether_down = {
+                let mut ether_down = test.clone();
+                ether_down.link = None;
+                ether_down
+            };
+
+            // serialize to buffer
+            let buffer = ether_down.serialize();
+
+            // PacketHeaders::from_ether_type
+            ether_down.assert_headers(
+                PacketHeaders::from_ether_type(
+                    test.link.clone().unwrap().ethernet2().unwrap().ether_type,
+                    &buffer[..],
+                )
+                .unwrap(),
+            );
+
+            // SlicedPacket::from_ether_type
+            ether_down.assert_sliced_packet(
+                SlicedPacket::from_ether_type(
+                    test.link.clone().unwrap().ethernet2().unwrap().ether_type,
+                    &buffer[..],
+                )
+                .unwrap(),
+            );
+
+            // create unexpected end of slice errors for the different headers
+            for len in ether_down.invalid_ser_lengths() {
+                if let Some(len) = len {
+                    assert!(PacketHeaders::from_ether_type(
+                        test.link.clone().unwrap().ethernet2().unwrap().ether_type,
+                        &buffer[..len]
+                    )
+                    .is_err());
+                    assert!(SlicedPacket::from_ether_type(
+                        test.link.clone().unwrap().ethernet2().unwrap().ether_type,
+                        &buffer[..len]
+                    )
+                    .is_err());
+                }
+            }
+        }
+
+        // packet from the internet layer down (without ethernet2 & vlan headers)
+        if test.ip.is_some() {
+            // serialize from the ip layer downwards
+            let ip_down = {
+                let mut ip_down = test.clone();
+                ip_down.link = None;
+                ip_down.vlan = None;
+                ip_down
+            };
+
+            // serialize to buffer
+            let buffer = ip_down.serialize();
+
+            // PacketHeaders::from_ip_slice
+            ip_down.assert_headers(PacketHeaders::from_ip_slice(&buffer).unwrap());
+
+            // SlicedPacket::from_ip
+            ip_down.assert_sliced_packet(SlicedPacket::from_ip(&buffer).unwrap());
+
+            // create unexpected end of slice errors for the different headers
+            for len in ip_down.invalid_ser_lengths() {
+                if let Some(len) = len {
+                    assert!(PacketHeaders::from_ip_slice(&buffer[..len]).is_err());
+                    assert!(SlicedPacket::from_ip(&buffer[..len]).is_err());
+                }
+            }
+        }
+    }
+
+    /// Creates slice lengths at which an too short slice error
+    /// should be triggered.
+    fn invalid_ser_lengths(&self) -> [Option<usize>; 12] {
+        struct Builder {
+            result: [Option<usize>; 12],
+            next_index: usize,
+            offset: usize,
+        }
+
+        impl Builder {
+            fn add(&mut self, header_len: usize) {
+                self.offset += header_len;
+                self.result[self.next_index] = Some(self.offset - 1);
+                self.next_index += 1;
+            }
+        }
+
+        let mut builder = Builder {
+            result: [None; 12],
+            next_index: 0,
+            offset: 0,
+        };
+
+        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());
+                }
+            }
+        }
+        if let Some(ip) = self.ip.as_ref() {
+            use IpHeaders::*;
+            match ip {
+                Ipv4(header, exts) => {
+                    builder.add(header.header_len());
+                    if let Some(auth) = exts.auth.as_ref() {
+                        builder.add(auth.header_len());
+                    }
+                }
+                Ipv6(header, exts) => {
+                    builder.add(header.header_len());
+                    if let Some(e) = exts.hop_by_hop_options.as_ref() {
+                        builder.add(e.header_len());
+                    }
+                    if let Some(e) = exts.destination_options.as_ref() {
+                        builder.add(e.header_len());
+                    }
+                    if let Some(routing) = exts.routing.as_ref() {
+                        builder.add(routing.routing.header_len());
+                        if let Some(e) = routing.final_destination_options.as_ref() {
+                            builder.add(e.header_len());
+                        }
+                    }
+                    if let Some(e) = exts.fragment.as_ref() {
+                        builder.add(e.header_len());
+                    }
+                    if let Some(e) = exts.auth.as_ref() {
+                        builder.add(e.header_len());
+                    }
+                }
+            }
+        }
+        if let Some(transport) = self.transport.as_ref() {
+            builder.add(transport.header_len());
+        }
+
+        builder.result
+    }
+
+    fn assert_headers(&self, actual: PacketHeaders) {
+        assert_eq!(self.link, actual.link);
+        assert_eq!(self.vlan, actual.vlan);
+        assert_eq!(self.ip, self.ip);
+        assert_eq!(self.transport, actual.transport);
+        assert_eq!(self.payload[..], actual.payload.slice()[..]);
+    }
+
+    fn assert_sliced_packet(&self, result: SlicedPacket) {
+        //assert identity to touch the derives (code coverage hack)
+        assert_eq!(result, result);
+
+        //ethernet & vlan
+        assert_eq!(
+            self.link,
+            match result.link.as_ref() {
+                Some(l) => match l {
+                    LinkSlice::Ethernet2(e) => Some(LinkHeader::Ethernet2(e.to_header())),
+                    LinkSlice::LinuxSll(e) => Some(LinkHeader::LinuxSll(e.to_header())),
+                    LinkSlice::EtherPayload(_) => None,
+                    LinkSlice::LinuxSllPayload(_) => None,
+                },
+                None => None,
+            }
+        ); //.unwrap_or(None).map(|ref x| x.to_header()));
+        assert_eq!(self.vlan, result.vlan.as_ref().map(|ref x| x.to_header()));
+
+        //ip
+        assert_eq!(self.ip, {
+            use crate::NetSlice::*;
+            match result.net.as_ref() {
+                Some(Ipv4(actual)) => Some(IpHeaders::Ipv4(
+                    actual.header().to_header(),
+                    Ipv4Extensions {
+                        auth: actual.extensions().auth.map(|ref x| x.to_header()),
+                    },
+                )),
+                Some(Ipv6(actual)) => Some(IpHeaders::Ipv6(
+                    actual.header().to_header(),
+                    Ipv6Extensions::from_slice(
+                        actual.header().next_header(),
+                        actual.extensions().slice(),
+                    )
+                    .unwrap()
+                    .0,
+                )),
+                None => None,
+            }
+        });
+
+        // transport header
+        assert_eq!(
+            self.transport,
+            match result.transport.as_ref() {
+                Some(TransportSlice::Icmpv4(actual)) =>
+                    Some(TransportHeader::Icmpv4(actual.header())),
+                Some(TransportSlice::Icmpv6(actual)) =>
+                    Some(TransportHeader::Icmpv6(actual.header())),
+                Some(TransportSlice::Udp(actual)) => Some(TransportHeader::Udp(actual.to_header())),
+                Some(TransportSlice::Tcp(actual)) => Some(TransportHeader::Tcp(actual.to_header())),
+                None => None,
+            }
+        );
+        // additional check for the contents of Unknown
+        if self.transport.is_none() {
+            match result.transport.as_ref() {
+                None => assert!(result.transport.is_none()),
+                _ => unreachable!(),
+            }
+        }
+
+        //payload
+        match result.transport.as_ref() {
+            Some(TransportSlice::Icmpv4(icmpv4)) => {
+                assert_eq!(&self.payload[..], icmpv4.payload());
+            }
+            Some(TransportSlice::Icmpv6(icmpv6)) => {
+                assert_eq!(&self.payload[..], icmpv6.payload());
+            }
+            Some(TransportSlice::Udp(udp)) => {
+                assert_eq!(&self.payload[..], udp.payload());
+            }
+            Some(TransportSlice::Tcp(tcp)) => {
+                assert_eq!(&self.payload[..], tcp.payload());
+            }
+            // check ip next
+            None => {
+                if let Some(ip) = result.net.as_ref() {
+                    assert_eq!(
+                        &self.payload[..],
+                        match ip {
+                            NetSlice::Ipv4(s) => s.payload.payload,
+                            NetSlice::Ipv6(s) => s.payload.payload,
+                        }
+                    );
+                } else {
+                    if let Some(vlan) = result.vlan.as_ref() {
+                        assert_eq!(&self.payload[..], vlan.payload().payload);
+                    } else {
+                        if let Some(LinkSlice::Ethernet2(eth)) = result.link.as_ref() {
+                            assert_eq!(&self.payload[..], eth.payload().payload);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    fn run_vlan(
+        &self,
+        outer_vlan: &SingleVlanHeader,
+        inner_vlan: &SingleVlanHeader,
+        ipv4: &Ipv4Header,
+        ipv4_ext: &Ipv4Extensions,
+        ipv6: &Ipv6Header,
+        ipv6_ext: &Ipv6Extensions,
+        udp: &UdpHeader,
+        tcp: &TcpHeader,
+        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
+            };
+
+        //single
+        setup_single(inner_vlan.ether_type).run();
+        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::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);
+        }
+    }
+
+    fn run_ipv4(
+        &self,
+        ip: &Ipv4Header,
+        ip_exts: &Ipv4Extensions,
+        udp: &UdpHeader,
+        tcp: &TcpHeader,
+        icmpv4: &Icmpv4Header,
+        icmpv6: &Icmpv6Header,
+    ) {
+        // fragmenting
+        {
+            let mut test = self.clone();
+            test.ip = Some({
+                let mut frag = ip.clone();
+                if false == frag.is_fragmenting_payload() {
+                    frag.more_fragments = true;
+                }
+                let mut header = IpHeaders::Ipv4(frag, ip_exts.clone());
+                header.set_next_headers(ip.protocol);
+                header
+            });
+
+            // run without transport header
+            test.run();
+        }
+
+        // non fragmenting
+        {
+            let mut test = self.clone();
+            test.ip = Some({
+                let mut non_frag = ip.clone();
+                non_frag.more_fragments = false;
+                non_frag.fragment_offset = 0.try_into().unwrap();
+                let mut header = IpHeaders::Ipv4(non_frag, ip_exts.clone());
+                header.set_next_headers(ip.protocol);
+                header
+            });
+            test.run_transport(udp, tcp, icmpv4, icmpv6);
+        }
+    }
+
+    fn run_ipv6(
+        &self,
+        ip: &Ipv6Header,
+        ip_exts: &Ipv6Extensions,
+        udp: &UdpHeader,
+        tcp: &TcpHeader,
+        icmpv4: &Icmpv4Header,
+        icmpv6: &Icmpv6Header,
+    ) {
+        // fragmenting
+        {
+            let mut test = self.clone();
+            test.ip = Some({
+                let mut frag = ip_exts.clone();
+                if let Some(frag) = frag.fragment.as_mut() {
+                    if false == frag.is_fragmenting_payload() {
+                        frag.more_fragments = true;
+                    }
+                } else {
+                    frag.fragment = Some(Ipv6FragmentHeader::new(
+                        ip_number::UDP,
+                        IpFragOffset::ZERO,
+                        true,
+                        0,
+                    ));
+                }
+                let mut header = IpHeaders::Ipv6(ip.clone(), frag);
+                header.set_next_headers(ip.next_header);
+                header
+            });
+            test.run();
+        }
+
+        // non fragmenting
+        {
+            let mut test = self.clone();
+            test.ip = Some({
+                let mut non_frag = ip_exts.clone();
+                non_frag.fragment = None;
+                let mut header = IpHeaders::Ipv6(ip.clone(), non_frag);
+                header.set_next_headers(ip.next_header);
+                header
+            });
+            test.run_transport(udp, tcp, icmpv4, icmpv6);
+        }
+    }
+
+    fn run_transport(
+        &self,
+        udp: &UdpHeader,
+        tcp: &TcpHeader,
+        icmpv4: &Icmpv4Header,
+        icmpv6: &Icmpv6Header,
+    ) {
+        // unknown transport layer
+        self.run();
+
+        // udp
+        {
+            let mut test = self.clone();
+            test.ip.as_mut().unwrap().set_next_headers(ip_number::UDP);
+            test.transport = Some(TransportHeader::Udp(udp.clone()));
+            test.run()
+        }
+
+        // tcp
+        {
+            let mut test = self.clone();
+            test.ip.as_mut().unwrap().set_next_headers(ip_number::TCP);
+            test.transport = Some(TransportHeader::Tcp(tcp.clone()));
+            test.run()
+        }
+
+        // icmpv4
+        if let Some(payload_size) = icmpv4.fixed_payload_size() {
+            let mut test = self.clone();
+            test.ip.as_mut().unwrap().set_next_headers(ip_number::ICMP);
+            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);
+            test.run()
+        } else {
+            let mut test = self.clone();
+            test.ip.as_mut().unwrap().set_next_headers(ip_number::ICMP);
+            test.transport = Some(TransportHeader::Icmpv4(icmpv4.clone()));
+            test.run()
+        }
+
+        // icmpv6
+        if let Some(payload_size) = icmpv6.fixed_payload_size() {
+            let mut test = self.clone();
+            test.ip
+                .as_mut()
+                .unwrap()
+                .set_next_headers(ip_number::IPV6_ICMP);
+            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);
+            test.run()
+        } else {
+            let mut test = self.clone();
+            test.ip
+                .as_mut()
+                .unwrap()
+                .set_next_headers(ip_number::IPV6_ICMP);
+            test.transport = Some(TransportHeader::Icmpv6(icmpv6.clone()));
+            test.run()
+        }
+    }
+}
+
+proptest! {
+    ///Test that all known packet compositions are parsed correctly.
+    #[test]
+    #[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 ipv4 in ipv4_unknown(),
+                         ref ipv4_exts in ipv4_extensions_unknown(),
+                         ref ipv6 in ipv6_unknown(),
+                         ref ipv6_exts in ipv6_extensions_unknown(),
+                         ref udp in udp_any(),
+                         ref tcp in tcp_any(),
+                         ref icmpv4 in icmpv4_header_any(),
+                         ref icmpv6 in icmpv6_header_any(),
+                         ref payload in proptest::collection::vec(any::<u8>(), 0..1024))
+    {
+        let setup_eth = | ether_type: EtherType | -> ComponentTest {
+            ComponentTest {
+                payload: payload.clone(),
+                link: Some({
+                    let mut result = eth.clone();
+                    result.ether_type = ether_type;
+                    LinkHeader::Ethernet2(result)
+                }),
+                vlan: None,
+                ip: None,
+                transport: None
+            }
+        };
+
+        //ethernet 2: standalone, ipv4, ipv6
+        setup_eth(eth.ether_type).run();
+        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);
+
+        //vlans
+        for ether_type in VLAN_ETHER_TYPES {
+            setup_eth(*ether_type).run_vlan(vlan_outer, vlan_inner, ipv4, ipv4_exts, ipv6, ipv6_exts, udp, tcp, icmpv4, icmpv6);
+        }
+    }
+}
+
+///Test that assert_sliced_packet is panicking when the ethernet header is missing
+#[test]
+#[should_panic]
+fn test_packet_slicing_panics() {
+    let s = SlicedPacket {
+        link: None,
+        vlan: None,
+        net: None,
+        transport: None,
+    };
+    ComponentTest {
+        link: Some(LinkHeader::Ethernet2(Ethernet2Header {
+            source: [0; 6],
+            destination: [0; 6],
+            ether_type: 0.into(),
+        })),
+        vlan: None,
+        ip: None,
+        transport: None,
+        payload: vec![],
+    }
+    .assert_sliced_packet(s);
+}
diff --git a/src/defrag/ip_defrag_buf.rs b/src/defrag/ip_defrag_buf.rs
new file mode 100644
index 0000000..8d4b9e3
--- /dev/null
+++ b/src/defrag/ip_defrag_buf.rs
@@ -0,0 +1,388 @@
+use crate::{defrag::*, *};
+use std::vec::Vec;
+
+/// Buffer to reconstruct a single fragmented IP packet.
+#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
+pub struct IpDefragBuf {
+    /// IP number identifying the type of payload.
+    ip_number: IpNumber,
+
+    /// Data buffer that should contain the SOMEIP header + reconstructed payload in the end.
+    data: Vec<u8>,
+
+    /// Contains the ranges filled with data.
+    sections: Vec<IpFragRange>,
+
+    /// End length of the defragmented packet (set if a packet with )
+    end: Option<u16>,
+}
+
+impl IpDefragBuf {
+    pub fn new(
+        ip_number: IpNumber,
+        mut data: Vec<u8>,
+        mut sections: Vec<IpFragRange>,
+    ) -> IpDefragBuf {
+        IpDefragBuf {
+            ip_number,
+            data: {
+                data.clear();
+                data
+            },
+            sections: {
+                sections.clear();
+                sections
+            },
+            end: None,
+        }
+    }
+
+    /// Return the ip number of the payload data that gets restored.
+    #[inline]
+    pub fn ip_number(&self) -> IpNumber {
+        self.ip_number
+    }
+
+    /// Data buffer in which data packet is reconstructed.
+    #[inline]
+    pub fn data(&self) -> &Vec<u8> {
+        &self.data
+    }
+
+    /// Sections completed of the packet.
+    #[inline]
+    pub fn sections(&self) -> &Vec<IpFragRange> {
+        &self.sections
+    }
+
+    /// Sections completed of the packet.
+    #[inline]
+    pub fn end(&self) -> Option<u16> {
+        self.end
+    }
+
+    /// Add a IPv4 slice
+    #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
+    pub fn add(
+        &mut self,
+        offset: IpFragOffset,
+        more_fragments: bool,
+        payload: &[u8],
+    ) -> Result<(), IpDefragError> {
+        use IpDefragError::*;
+
+        // validate lengths
+        let Ok(len_u16) = u16::try_from(payload.len()) else {
+            return Err(SegmentTooBig {
+                offset,
+                payload_len: payload.len(),
+                max: MAX_IP_DEFRAG_LEN_U16,
+            });
+        };
+
+        let Some(end) = offset.byte_offset().checked_add(len_u16) else {
+            return Err(SegmentTooBig {
+                offset,
+                payload_len: payload.len(),
+                max: MAX_IP_DEFRAG_LEN_U16,
+            });
+        };
+
+        // validate that the payload len is a multiple of 8 in case it is not the end
+        if more_fragments && 0 != payload.len() & 0b111 {
+            return Err(UnalignedFragmentPayloadLen {
+                offset,
+                payload_len: payload.len(),
+            });
+        }
+
+        // check the section is not already ended
+        if let Some(previous_end) = self.end {
+            // either the end is after the current position
+            if previous_end < end || ((false == more_fragments) && end != previous_end) {
+                return Err(ConflictingEnd {
+                    previous_end,
+                    conflicting_end: end,
+                });
+            }
+        }
+
+        // get enough memory to store the de-fragmented
+        let required_len = usize::from(end);
+        if self.data.len() < required_len {
+            if self.data.capacity() < required_len
+                && self
+                    .data
+                    .try_reserve(required_len - self.data.len())
+                    .is_err()
+            {
+                return Err(AllocationFailure { len: required_len });
+            }
+            unsafe {
+                self.data.set_len(required_len);
+            }
+        }
+
+        // insert new data
+        let data_offset = usize::from(offset.byte_offset());
+        self.data[data_offset..data_offset + payload.len()].copy_from_slice(payload);
+
+        // update sections
+        let mut new_section = IpFragRange {
+            start: offset.byte_offset(),
+            end,
+        };
+
+        // merge overlapping section into new section and remove them
+        self.sections.retain(|it| -> bool {
+            if let Some(merged) = new_section.merge(*it) {
+                new_section = merged;
+                false
+            } else {
+                true
+            }
+        });
+        self.sections.push(new_section);
+
+        // set end
+        if false == more_fragments {
+            self.end = Some(end);
+            // restrict the length based on the length
+            unsafe {
+                // SAFETY: Safe as the length has previously been checked to be at least "end" long
+                self.data.set_len(usize::from(end));
+            }
+        }
+
+        Ok(())
+    }
+
+    /// Returns true if the fragmented data is completed.
+    pub fn is_complete(&self) -> bool {
+        self.end.is_some() && 1 == self.sections.len() && 0 == self.sections[0].start
+    }
+
+    /// Consume the [`IpDefragBuf`] and return the buffers.
+    #[inline]
+    pub fn take_bufs(self) -> (Vec<u8>, Vec<IpFragRange>) {
+        (self.data, self.sections)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use std::{format, vec};
+
+    #[test]
+    fn debug_clone_eq() {
+        let buf = IpDefragBuf::new(IpNumber::UDP, Vec::new(), Vec::new());
+        let _ = format!("{:?}", buf);
+        assert_eq!(buf, buf.clone());
+        assert_eq!(buf.cmp(&buf), core::cmp::Ordering::Equal);
+        assert_eq!(buf.partial_cmp(&buf), Some(core::cmp::Ordering::Equal));
+
+        use core::hash::{Hash, Hasher};
+        use std::collections::hash_map::DefaultHasher;
+        let h1 = {
+            let mut h = DefaultHasher::new();
+            buf.hash(&mut h);
+            h.finish()
+        };
+        let h2 = {
+            let mut h = DefaultHasher::new();
+            buf.clone().hash(&mut h);
+            h.finish()
+        };
+        assert_eq!(h1, h2);
+    }
+
+    #[test]
+    fn new() {
+        let actual = IpDefragBuf::new(
+            IpNumber::UDP,
+            vec![1],
+            vec![IpFragRange { start: 0, end: 1 }],
+        );
+        assert_eq!(actual.ip_number(), IpNumber::UDP);
+        assert!(actual.data().is_empty());
+        assert!(actual.sections().is_empty());
+        assert!(actual.end().is_none());
+    }
+
+    /// Returns a u8 vec counting up from "start" until len is reached (truncating bits greater then u8).
+    fn sequence(start: usize, len: usize) -> Vec<u8> {
+        let mut result = Vec::with_capacity(len);
+        for i in start..start + len {
+            result.push((i & 0xff) as u8);
+        }
+        result
+    }
+
+    #[rustfmt::skip]
+    #[test]
+    fn add() {
+        use IpDefragError::*;
+
+        // normal reconstruction
+        {
+            let mut buffer = IpDefragBuf::new(IpNumber::UDP, Vec::new(), Vec::new());
+
+            let actions = [
+                (false, (0, true, &sequence(0,16))),
+                (false, (16, true, &sequence(16,32))),
+                (true, (48, false, &sequence(48,16))),
+            ];
+            for a in actions {
+                assert!(0 == (a.1.0 % 8));
+                buffer.add(
+                    IpFragOffset::try_new(a.1.0 / 8).unwrap(),
+                    a.1.1,
+                    a.1.2
+                ).unwrap();
+                assert_eq!(a.0, buffer.is_complete());
+            }
+            let (payload, _) = buffer.take_bufs();
+            assert_eq!(&payload, &sequence(0,16*4));
+        }
+
+        // overlapping reconstruction
+        {
+            let mut buffer = IpDefragBuf::new(IpNumber::UDP, Vec::new(), Vec::new());
+
+            let actions = [
+                (false, (0, true, sequence(0,16))),
+                // will be overwritten
+                (false, (32, true, sequence(0,16))),
+                // overwrites
+                (false, (32, false, sequence(32,16))),
+                // completes
+                (true, (16, true, sequence(16,16))),
+            ];
+            for a in actions {
+                assert!(0 == (a.1.0 % 8));
+                buffer.add(
+                    IpFragOffset::try_new(a.1.0 / 8).unwrap(),
+                    a.1.1,
+                    &a.1.2
+                ).unwrap();
+                assert_eq!(a.0, buffer.is_complete());
+            }
+            let (payload, _) = buffer.take_bufs();
+            assert_eq!(&payload, &sequence(0,16*3));
+        }
+
+        // reverse order
+        {
+            let mut buffer = IpDefragBuf::new(IpNumber::UDP, Vec::new(), Vec::new());
+
+            let actions = [
+                (false, (48, false, &sequence(48,16))),
+                (false, (16, true, &sequence(16,32))),
+                (true, (0, true, &sequence(0,16))),
+            ];
+            for a in actions {
+                assert!(0 == (a.1.0 % 8));
+                buffer.add(
+                    IpFragOffset::try_new(a.1.0 / 8).unwrap(),
+                    a.1.1,
+                    &a.1.2
+                ).unwrap();
+                assert_eq!(a.0, buffer.is_complete());
+            }
+            let (payload, _) = buffer.take_bufs();
+            assert_eq!(&payload, &sequence(0,16*4));
+        }
+
+        // error packet bigger then max (payload len only)
+        {
+            let mut buffer = IpDefragBuf::new(IpNumber::UDP, Vec::new(), Vec::new());
+            let payload_len = usize::from(u16::MAX) + 1;
+            assert_eq!(
+                SegmentTooBig { offset: IpFragOffset::try_new(0).unwrap(), payload_len, max: u16::MAX },
+                buffer.add(
+                    IpFragOffset::try_new(0).unwrap(),
+                    true,
+                    &sequence(0, payload_len)
+                ).unwrap_err()
+            );
+        }
+
+        // error packet bigger then max (offset + payload len)
+        {
+            let mut buffer = IpDefragBuf::new(IpNumber::UDP, Vec::new(), Vec::new());
+            let payload_len = usize::from(u16::MAX) - 32 - 16 + 1;
+            assert_eq!(
+                SegmentTooBig { offset: IpFragOffset::try_new((32 + 16)/8).unwrap(), payload_len, max: u16::MAX },
+                buffer.add(
+                    IpFragOffset::try_new((32 + 16)/8).unwrap(),
+                    true,
+                    &sequence(0,payload_len)
+                ).unwrap_err()
+            );
+        }
+
+        // check packets that fill exactly to the max work
+        {
+            let mut buffer = IpDefragBuf::new(IpNumber::UDP, Vec::new(), Vec::new());
+
+            let payload_len = usize::from(u16::MAX - 16);
+            assert_eq!(
+                Ok(()),
+                buffer.add(
+                    IpFragOffset::try_new(16/8).unwrap(),
+                    false,
+                    &sequence(0, payload_len)
+                )
+            );
+        }
+
+        // packets conflicting with previously seen end
+        for bad_offset in 1..8 {
+            let mut buffer = IpDefragBuf::new(IpNumber::UDP, Vec::new(), Vec::new());
+            assert_eq!(
+                UnalignedFragmentPayloadLen {
+                    offset: IpFragOffset::try_new(48/8).unwrap(),
+                    payload_len: bad_offset
+                },
+                buffer.add(
+                    IpFragOffset::try_new(48/8).unwrap(),
+                    true,
+                    &sequence(0, bad_offset)
+                ).unwrap_err()
+            );
+        }
+
+        // test that conflicting ends trigger errors (received a different end)
+        {
+            let mut buffer = IpDefragBuf::new(IpNumber::UDP, Vec::new(), Vec::new());
+
+            // setup an end (aka no more segements)
+            buffer.add(
+                IpFragOffset::try_new(32/8).unwrap(),
+                false,
+                &sequence(32,16)
+            ).unwrap();
+
+            // test that a "non end" going over the end package triggers an error
+            assert_eq!(
+                ConflictingEnd { previous_end: 32 + 16, conflicting_end: 48 + 16 },
+                buffer.add(
+                    IpFragOffset::try_new(48/8).unwrap(),
+                    true,
+                    &sequence(48,16)
+                ).unwrap_err()
+            );
+
+            // test that a new end at an earlier position triggers an error
+            assert_eq!(
+                ConflictingEnd { previous_end: 32 + 16, conflicting_end: 16 + 16 },
+                buffer.add(
+                    IpFragOffset::try_new(16/8).unwrap(),
+                    false,
+                    &sequence(16,16)
+                ).unwrap_err()
+            );
+        }
+    }
+}
diff --git a/src/defrag/ip_defrag_error.rs b/src/defrag/ip_defrag_error.rs
new file mode 100644
index 0000000..d5bc2b7
--- /dev/null
+++ b/src/defrag/ip_defrag_error.rs
@@ -0,0 +1,111 @@
+use crate::*;
+
+#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub enum IpDefragError {
+    /// Error if a payload lenght of a IP Fragment packet is not a multiple of 16
+    /// and the "more fragments" flag is set.
+    UnalignedFragmentPayloadLen {
+        offset: IpFragOffset,
+        payload_len: usize,
+    },
+
+    /// Error if a segment is bigger then the maximum allowed size.
+    SegmentTooBig {
+        offset: IpFragOffset,
+        payload_len: usize,
+        max: u16,
+    },
+
+    /// Error if multiple TP segments were received with the "more segment"
+    /// unset and differing end points.
+    ConflictingEnd {
+        /// Offset + tp_payload.len() of the previous package with "more segment" unset.
+        previous_end: u16,
+
+        /// Offset + tp_payload.len() of the current package.
+        conflicting_end: u16,
+    },
+
+    /// Error if not enough memory could be allocated to store the TP payload.
+    AllocationFailure { len: usize },
+}
+
+impl core::fmt::Display for IpDefragError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        use IpDefragError::*;
+        match self {
+            UnalignedFragmentPayloadLen{ offset, payload_len } => write!(f, "Payload length {payload_len} of IP fragment (offset {offset}) is not a multiple of 8. This is only allowed for the last fragment packet."),
+            SegmentTooBig{ offset, payload_len, max } => write!(f, "Overall length of IP fragment (offset {offset}, payload len: {payload_len}) bigger then the maximum allowed size of {max}."),
+            ConflictingEnd { previous_end, conflicting_end } => write!(f, "Received a IP fragment (offset + len: {conflicting_end}) which conflicts a package that previously set the end to {previous_end}."),
+            AllocationFailure { len } => write!(f, "Failed to allocate {len} bytes of memory to reconstruct the fragmented IP packets."),
+        }
+    }
+}
+
+impl std::error::Error for IpDefragError {}
+
+#[cfg(test)]
+mod tests {
+    use super::IpDefragError::*;
+    use super::*;
+    use std::format;
+
+    #[test]
+    fn debug() {
+        let err = UnalignedFragmentPayloadLen {
+            offset: IpFragOffset::try_new(0).unwrap(),
+            payload_len: 16,
+        };
+        let _ = format!("{err:?}");
+    }
+
+    #[test]
+    fn clone_eq_hash_ord() {
+        use core::cmp::Ordering;
+        use std::collections::hash_map::DefaultHasher;
+        use std::hash::{Hash, Hasher};
+
+        let err = UnalignedFragmentPayloadLen {
+            offset: IpFragOffset::try_new(0).unwrap(),
+            payload_len: 16,
+        };
+        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);
+        assert_eq!(Ordering::Equal, err.cmp(&err));
+        assert_eq!(Some(Ordering::Equal), err.partial_cmp(&err));
+    }
+
+    #[test]
+    fn fmt() {
+        let tests = [
+            (UnalignedFragmentPayloadLen { offset: IpFragOffset::try_new(0).unwrap(), payload_len: 2 }, "Payload length 2 of IP fragment (offset 0) is not a multiple of 8. This is only allowed for the last fragment packet."),
+            (SegmentTooBig { offset: IpFragOffset::try_new(0).unwrap(), payload_len: 2, max: 3, }, "Overall length of IP fragment (offset 0, payload len: 2) bigger then the maximum allowed size of 3."),
+            (ConflictingEnd { previous_end: 2, conflicting_end: 1 }, "Received a IP fragment (offset + len: 1) which conflicts a package that previously set the end to 2."),
+            (AllocationFailure { len: 0 }, "Failed to allocate 0 bytes of memory to reconstruct the fragmented IP packets."),
+        ];
+        for test in tests {
+            assert_eq!(format!("{}", test.0), test.1);
+        }
+    }
+
+    #[test]
+    fn source() {
+        use std::error::Error;
+        assert!(UnalignedFragmentPayloadLen {
+            offset: IpFragOffset::try_new(0).unwrap(),
+            payload_len: 16
+        }
+        .source()
+        .is_none());
+    }
+}
diff --git a/src/defrag/ip_defrag_payload_vec.rs b/src/defrag/ip_defrag_payload_vec.rs
new file mode 100644
index 0000000..c94984d
--- /dev/null
+++ b/src/defrag/ip_defrag_payload_vec.rs
@@ -0,0 +1,67 @@
+use crate::*;
+use std::vec::Vec;
+
+/// Payload of an IP packet.
+#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
+pub struct IpDefragPayloadVec {
+    /// Identifying content of the payload.
+    pub ip_number: IpNumber,
+
+    /// Length field that was used to determine the length
+    /// of the payload (e.g. IPv6 "payload_length" field).
+    pub len_source: LenSource,
+
+    /// Payload
+    pub payload: Vec<u8>,
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use std::{format, vec};
+
+    #[test]
+    fn debug() {
+        let s = IpDefragPayloadVec {
+            ip_number: IpNumber::UDP,
+            len_source: LenSource::Slice,
+            payload: vec![],
+        };
+        assert_eq!(
+            format!(
+                "IpDefragPayloadVec {{ ip_number: {:?}, len_source: {:?}, payload: {:?} }}",
+                s.ip_number, s.len_source, s.payload
+            ),
+            format!("{:?}", s)
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash_ord() {
+        let s = IpDefragPayloadVec {
+            ip_number: IpNumber::UDP,
+            len_source: LenSource::Slice,
+            payload: vec![],
+        };
+        assert_eq!(s.clone(), s);
+
+        use std::collections::hash_map::DefaultHasher;
+        use std::hash::{Hash, Hasher};
+
+        let a_hash = {
+            let mut hasher = DefaultHasher::new();
+            s.hash(&mut hasher);
+            hasher.finish()
+        };
+        let b_hash = {
+            let mut hasher = DefaultHasher::new();
+            s.clone().hash(&mut hasher);
+            hasher.finish()
+        };
+        assert_eq!(a_hash, b_hash);
+
+        use std::cmp::Ordering;
+        assert_eq!(s.clone().cmp(&s), Ordering::Equal);
+        assert_eq!(s.clone().partial_cmp(&s), Some(Ordering::Equal));
+    }
+}
diff --git a/src/defrag/ip_defrag_pool.rs b/src/defrag/ip_defrag_pool.rs
new file mode 100644
index 0000000..d7c2f19
--- /dev/null
+++ b/src/defrag/ip_defrag_pool.rs
@@ -0,0 +1,675 @@
+use crate::{defrag::*, *};
+use std::collections::HashMap;
+use std::vec::Vec;
+
+/// Pool of buffers to reconstruct multiple fragmented IP packets in
+/// parallel (re-uses buffers to minimize allocations).
+///
+/// It differentiates the packets based on their inner & outer vlan as well as
+/// source and destination ip address and allows the user to add their own
+/// custom "channel id" type to further differentiate different streams.
+/// The custom channel id can be used to
+///
+/// # This implementation is NOT safe against "Out of Memory" attacks
+///
+/// If you use the [`DefragPool`] in an untrusted environment an attacker could
+/// cause an "out of memory error" by opening up multiple parallel TP streams,
+/// never ending them and filling them up with as much data as possible.
+///
+/// Mitigations will hopefully be offered in future versions but if you have
+/// take care right now you can still use [`IpDefragBuf`] directly and implement the
+/// connection handling and mitigation yourself.
+#[derive(Debug, Clone)]
+pub struct IpDefragPool<Timestamp = (), CustomChannelId = ()>
+where
+    Timestamp: Sized + core::fmt::Debug + Clone,
+    CustomChannelId: Sized + core::fmt::Debug + Clone + core::hash::Hash + Eq + PartialEq,
+{
+    /// Currently reconstructing IP packets.
+    active: HashMap<IpFragId<CustomChannelId>, (IpDefragBuf, Timestamp)>,
+
+    /// Data buffers that have finished receiving data and can be re-used.
+    finished_data_bufs: Vec<Vec<u8>>,
+
+    /// Section buffers that have finished receiving data and can be re-used.
+    finished_section_bufs: Vec<Vec<IpFragRange>>,
+}
+
+impl<Timestamp, CustomChannelId> IpDefragPool<Timestamp, CustomChannelId>
+where
+    Timestamp: Sized + core::fmt::Debug + Clone,
+    CustomChannelId: Sized + core::fmt::Debug + Clone + core::hash::Hash + Eq + PartialEq,
+{
+    pub fn new() -> IpDefragPool<Timestamp, CustomChannelId> {
+        IpDefragPool {
+            active: HashMap::new(),
+            finished_data_bufs: Vec::new(),
+            finished_section_bufs: Vec::new(),
+        }
+    }
+
+    /// Add data from a sliced packet.
+    pub fn process_sliced_packet(
+        &mut self,
+        slice: &SlicedPacket,
+        timestamp: Timestamp,
+        channel_id: CustomChannelId,
+    ) -> Result<Option<IpDefragPayloadVec>, IpDefragError> {
+        // extract the fragment related data and skip non-fragmented packets
+        let (frag_id, offset, more_fragments, payload, is_ipv4) = match &slice.net {
+            Some(NetSlice::Ipv4(ipv4)) => {
+                let header = ipv4.header();
+                if false == header.is_fragmenting_payload() {
+                    // nothing to defragment here, skip packet
+                    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,
+                        ip: IpFragVersionSpecId::Ipv4 {
+                            source: header.source(),
+                            destination: header.destination(),
+                            identification: header.identification(),
+                        },
+                        payload_ip_number: ipv4.payload().ip_number,
+                        channel_id,
+                    },
+                    header.fragments_offset(),
+                    header.more_fragments(),
+                    ipv4.payload(),
+                    true,
+                )
+            }
+            Some(NetSlice::Ipv6(ipv6)) => {
+                // get fragmentation header
+                let frag = {
+                    let mut f = None;
+                    for ext in ipv6.extensions().clone().into_iter() {
+                        use Ipv6ExtensionSlice::*;
+                        if let Fragment(frag_it) = ext {
+                            f = Some(frag_it);
+                            break;
+                        }
+                    }
+                    if let Some(f) = f {
+                        if f.is_fragmenting_payload() {
+                            f.to_header()
+                        } else {
+                            // nothing to defragment here, skip packet
+                            return Ok(None);
+                        }
+                    } else {
+                        // nothing to defragment here, skip packet
+                        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),
+                };
+
+                // calculate frag id
+                (
+                    IpFragId {
+                        outer_vlan_id,
+                        inner_vlan_id,
+                        ip: IpFragVersionSpecId::Ipv6 {
+                            source: ipv6.header().source(),
+                            destination: ipv6.header().destination(),
+                            identification: frag.identification,
+                        },
+                        payload_ip_number: ipv6.payload().ip_number,
+                        channel_id,
+                    },
+                    frag.fragment_offset,
+                    frag.more_fragments,
+                    ipv6.payload(),
+                    false,
+                )
+            }
+            None => {
+                // nothing to defragment here, skip packet
+                return Ok(None);
+            }
+        };
+
+        // get the reconstruction buffer
+        use std::collections::hash_map::Entry;
+        match self.active.entry(frag_id) {
+            Entry::Occupied(mut entry) => {
+                let buf = entry.get_mut();
+                buf.0.add(offset, more_fragments, payload.payload)?;
+                buf.1 = timestamp;
+                if buf.0.is_complete() {
+                    let (defraged_payload, sections) = entry.remove().0.take_bufs();
+                    self.finished_section_bufs.push(sections);
+                    Ok(Some(IpDefragPayloadVec {
+                        ip_number: payload.ip_number,
+                        len_source: if is_ipv4 {
+                            LenSource::Ipv4HeaderTotalLen
+                        } else {
+                            LenSource::Ipv6HeaderPayloadLen
+                        },
+                        payload: defraged_payload,
+                    }))
+                } else {
+                    Ok(None)
+                }
+            }
+            Entry::Vacant(entry) => {
+                let data_buf = if let Some(mut d) = self.finished_data_bufs.pop() {
+                    d.clear();
+                    d
+                } else {
+                    Vec::with_capacity(payload.payload.len() * 2)
+                };
+                let sections = if let Some(mut s) = self.finished_section_bufs.pop() {
+                    s.clear();
+                    s
+                } else {
+                    Vec::with_capacity(4)
+                };
+
+                let mut defrag_buf = IpDefragBuf::new(payload.ip_number, data_buf, sections);
+                match defrag_buf.add(offset, more_fragments, payload.payload) {
+                    Ok(()) => {
+                        // no need to check if the defrag is done as the
+                        // packet can not be defragmented on initial add
+                        // otherwise `is_fragmenting_payload` would have
+                        // been false
+                        entry.insert((defrag_buf, timestamp));
+                        Ok(None)
+                    }
+                    Err(err) => {
+                        // return the buffers
+                        let (data_buf, sections) = defrag_buf.take_bufs();
+                        self.finished_data_bufs.push(data_buf);
+                        self.finished_section_bufs.push(sections);
+                        Err(err)
+                    }
+                }
+            }
+        }
+    }
+
+    /// Returns a buffer to the pool so it can be re-used.
+    pub fn return_buf(&mut self, buf: IpDefragPayloadVec) {
+        self.finished_data_bufs.push(buf.payload);
+    }
+
+    /// Retains only the elements specified by the predicate.
+    pub fn retain<F>(&mut self, f: F)
+    where
+        F: Fn(&Timestamp) -> bool,
+    {
+        if self.active.iter().any(|(_, (_, t))| false == f(t)) {
+            self.active = self
+                .active
+                .drain()
+                .filter_map(|(k, v)| {
+                    if f(&v.1) {
+                        Some((k, v))
+                    } else {
+                        let (data, sections) = v.0.take_bufs();
+                        self.finished_data_bufs.push(data);
+                        self.finished_section_bufs.push(sections);
+                        None
+                    }
+                })
+                .collect();
+        }
+    }
+}
+
+impl<Timestamp, CustomChannelId> Default for IpDefragPool<Timestamp, CustomChannelId>
+where
+    Timestamp: Sized + core::fmt::Debug + Clone,
+    CustomChannelId: Sized + core::fmt::Debug + Clone + core::hash::Hash + Eq + PartialEq,
+{
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use std::cmp::max;
+
+    use super::*;
+
+    #[test]
+    fn new() {
+        {
+            let pool = IpDefragPool::<(), ()>::new();
+            assert_eq!(pool.active.len(), 0);
+            assert_eq!(pool.finished_data_bufs.len(), 0);
+            assert_eq!(pool.finished_section_bufs.len(), 0);
+        }
+        {
+            let pool = IpDefragPool::<u32, (u32, u32)>::new();
+            assert_eq!(pool.active.len(), 0);
+            assert_eq!(pool.finished_data_bufs.len(), 0);
+            assert_eq!(pool.finished_section_bufs.len(), 0);
+        }
+    }
+
+    #[test]
+    fn default() {
+        {
+            let pool: IpDefragPool<(), ()> = Default::default();
+            assert_eq!(pool.active.len(), 0);
+            assert_eq!(pool.finished_data_bufs.len(), 0);
+            assert_eq!(pool.finished_section_bufs.len(), 0);
+        }
+        {
+            let pool: IpDefragPool<u32, (u32, u32)> = Default::default();
+            assert_eq!(pool.active.len(), 0);
+            assert_eq!(pool.finished_data_bufs.len(), 0);
+            assert_eq!(pool.finished_section_bufs.len(), 0);
+        }
+    }
+
+    fn build_packet<CustomChannelId: core::hash::Hash + Eq + PartialEq + Clone + Sized>(
+        id: IpFragId<CustomChannelId>,
+        offset: u16,
+        more: bool,
+        payload: &[u8],
+    ) -> Vec<u8> {
+        let mut buf = Vec::with_capacity(
+            Ethernet2Header::LEN
+                + SingleVlanHeader::LEN
+                + SingleVlanHeader::LEN
+                + max(
+                    Ipv4Header::MIN_LEN,
+                    Ipv6Header::LEN + Ipv6FragmentHeader::LEN,
+                )
+                + payload.len(),
+        );
+
+        let ip_ether_type = match id.ip {
+            IpFragVersionSpecId::Ipv4 {
+                source: _,
+                destination: _,
+                identification: _,
+            } => EtherType::IPV4,
+            IpFragVersionSpecId::Ipv6 {
+                source: _,
+                destination: _,
+                identification: _,
+            } => EtherType::IPV6,
+        };
+
+        buf.extend_from_slice(
+            &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 {
+                    ip_ether_type
+                },
+            }
+            .to_bytes(),
+        );
+
+        if let Some(vlan_id) = id.outer_vlan_id {
+            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() {
+                        EtherType::VLAN_TAGGED_FRAME
+                    } else {
+                        ip_ether_type
+                    },
+                }
+                .to_bytes(),
+            );
+        }
+
+        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,
+                destination,
+                identification,
+            } => {
+                let mut header = Ipv4Header {
+                    identification,
+                    more_fragments: more,
+                    fragment_offset: IpFragOffset::try_new(offset).unwrap(),
+                    protocol: id.payload_ip_number,
+                    source,
+                    destination,
+                    total_len: (Ipv4Header::MIN_LEN + payload.len()) as u16,
+                    time_to_live: 2,
+                    ..Default::default()
+                };
+                header.header_checksum = header.calc_header_checksum();
+                buf.extend_from_slice(&header.to_bytes());
+            }
+            IpFragVersionSpecId::Ipv6 {
+                source,
+                destination,
+                identification,
+            } => {
+                buf.extend_from_slice(
+                    &Ipv6Header {
+                        traffic_class: 0,
+                        flow_label: Default::default(),
+                        payload_length: (payload.len() + Ipv6FragmentHeader::LEN) as u16,
+                        next_header: IpNumber::IPV6_FRAGMENTATION_HEADER,
+                        hop_limit: 2,
+                        source,
+                        destination,
+                    }
+                    .to_bytes(),
+                );
+                buf.extend_from_slice(
+                    &Ipv6FragmentHeader {
+                        next_header: id.payload_ip_number,
+                        fragment_offset: IpFragOffset::try_new(offset).unwrap(),
+                        more_fragments: more,
+                        identification,
+                    }
+                    .to_bytes(),
+                );
+            }
+        }
+        buf.extend_from_slice(payload);
+        buf
+    }
+
+    #[test]
+    fn process_sliced_packet() {
+        // v4 non fragmented
+        {
+            let mut pool = IpDefragPool::<(), ()>::new();
+            let pdata = build_packet(
+                IpFragId {
+                    outer_vlan_id: None,
+                    inner_vlan_id: None,
+                    ip: IpFragVersionSpecId::Ipv4 {
+                        source: [0; 4],
+                        destination: [0; 4],
+                        identification: 0,
+                    },
+                    payload_ip_number: IpNumber::UDP,
+                    channel_id: (),
+                },
+                0,
+                false,
+                &UdpHeader {
+                    source_port: 0,
+                    destination_port: 0,
+                    length: 0,
+                    checksum: 0,
+                }
+                .to_bytes(),
+            );
+            let pslice = SlicedPacket::from_ethernet(&pdata).unwrap();
+            let v = pool.process_sliced_packet(&pslice, (), ());
+            assert_eq!(Ok(None), v);
+
+            // check the effect had no effect
+            assert_eq!(pool.active.len(), 0);
+            assert_eq!(pool.finished_data_bufs.len(), 0);
+            assert_eq!(pool.finished_section_bufs.len(), 0);
+        }
+
+        // v6 non fragmented
+        {
+            let mut pool = IpDefragPool::<(), ()>::new();
+            let pdata = build_packet(
+                IpFragId {
+                    outer_vlan_id: None,
+                    inner_vlan_id: None,
+                    ip: IpFragVersionSpecId::Ipv6 {
+                        source: [0; 16],
+                        destination: [0; 16],
+                        identification: 0,
+                    },
+                    payload_ip_number: IpNumber::UDP,
+                    channel_id: (),
+                },
+                0,
+                false,
+                &UdpHeader {
+                    source_port: 0,
+                    destination_port: 0,
+                    length: 0,
+                    checksum: 0,
+                }
+                .to_bytes(),
+            );
+            let pslice = SlicedPacket::from_ethernet(&pdata).unwrap();
+            let v = pool.process_sliced_packet(&pslice, (), ());
+            assert_eq!(Ok(None), v);
+
+            // check the effect had no effect
+            assert_eq!(pool.active.len(), 0);
+            assert_eq!(pool.finished_data_bufs.len(), 0);
+            assert_eq!(pool.finished_section_bufs.len(), 0);
+        }
+
+        // v4 & v6 basic test
+        {
+            let frag_ids = [
+                // v4 (no vlan)
+                IpFragId {
+                    outer_vlan_id: None,
+                    inner_vlan_id: None,
+                    ip: IpFragVersionSpecId::Ipv4 {
+                        source: [1, 2, 3, 4],
+                        destination: [5, 6, 7, 8],
+                        identification: 9,
+                    },
+                    payload_ip_number: IpNumber::UDP,
+                    channel_id: (),
+                },
+                // v4 (single vlan)
+                IpFragId {
+                    outer_vlan_id: Some(VlanId::try_new(12).unwrap()),
+                    inner_vlan_id: None,
+                    ip: IpFragVersionSpecId::Ipv4 {
+                        source: [1, 2, 3, 4],
+                        destination: [5, 6, 7, 8],
+                        identification: 9,
+                    },
+                    payload_ip_number: IpNumber::UDP,
+                    channel_id: (),
+                },
+                // v4 (double vlan)
+                IpFragId {
+                    outer_vlan_id: Some(VlanId::try_new(12).unwrap()),
+                    inner_vlan_id: Some(VlanId::try_new(23).unwrap()),
+                    ip: IpFragVersionSpecId::Ipv4 {
+                        source: [1, 2, 3, 4],
+                        destination: [5, 6, 7, 8],
+                        identification: 9,
+                    },
+                    payload_ip_number: IpNumber::UDP,
+                    channel_id: (),
+                },
+                // v6 (no vlan)
+                IpFragId {
+                    outer_vlan_id: None,
+                    inner_vlan_id: None,
+                    ip: IpFragVersionSpecId::Ipv6 {
+                        source: [0; 16],
+                        destination: [0; 16],
+                        identification: 0,
+                    },
+                    payload_ip_number: IpNumber::UDP,
+                    channel_id: (),
+                },
+                // v6 (single vlan)
+                IpFragId {
+                    outer_vlan_id: Some(VlanId::try_new(12).unwrap()),
+                    inner_vlan_id: None,
+                    ip: IpFragVersionSpecId::Ipv6 {
+                        source: [0; 16],
+                        destination: [0; 16],
+                        identification: 0,
+                    },
+                    payload_ip_number: IpNumber::UDP,
+                    channel_id: (),
+                },
+                // v6 (double vlan)
+                IpFragId {
+                    outer_vlan_id: Some(VlanId::try_new(12).unwrap()),
+                    inner_vlan_id: Some(VlanId::try_new(23).unwrap()),
+                    ip: IpFragVersionSpecId::Ipv6 {
+                        source: [0; 16],
+                        destination: [0; 16],
+                        identification: 0,
+                    },
+                    payload_ip_number: IpNumber::UDP,
+                    channel_id: (),
+                },
+            ];
+
+            let mut pool = IpDefragPool::<(), ()>::new();
+
+            for frag_id in frag_ids {
+                {
+                    let pdata = build_packet(frag_id.clone(), 0, true, &[1, 2, 3, 4, 5, 6, 7, 8]);
+                    let pslice = SlicedPacket::from_ethernet(&pdata).unwrap();
+                    let v = pool.process_sliced_packet(&pslice, (), ());
+                    assert_eq!(Ok(None), v);
+
+                    // check the frag id was correctly calculated
+                    assert_eq!(1, pool.active.len());
+                    assert_eq!(pool.active.iter().next().unwrap().0, &frag_id);
+                }
+
+                {
+                    let pdata = build_packet(frag_id.clone(), 1, false, &[9, 10]);
+                    let pslice = SlicedPacket::from_ethernet(&pdata).unwrap();
+                    let v = pool
+                        .process_sliced_packet(&pslice, (), ())
+                        .unwrap()
+                        .unwrap();
+                    assert_eq!(v.ip_number, IpNumber::UDP);
+                    assert_eq!(
+                        v.len_source,
+                        if matches!(
+                            frag_id.ip,
+                            IpFragVersionSpecId::Ipv4 {
+                                source: _,
+                                destination: _,
+                                identification: _
+                            }
+                        ) {
+                            LenSource::Ipv4HeaderTotalLen
+                        } else {
+                            LenSource::Ipv6HeaderPayloadLen
+                        }
+                    );
+                    assert_eq!(v.payload, &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+
+                    // there should be nothing left
+                    assert_eq!(pool.active.len(), 0);
+                    assert_eq!(pool.finished_data_bufs.len(), 0);
+                    assert_eq!(pool.finished_section_bufs.len(), 1);
+
+                    // return buffer
+                    pool.return_buf(v);
+
+                    assert_eq!(pool.active.len(), 0);
+                    assert_eq!(pool.finished_data_bufs.len(), 1);
+                    assert_eq!(pool.finished_section_bufs.len(), 1);
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn retain() {
+        let frag_id_0 = IpFragId {
+            outer_vlan_id: None,
+            inner_vlan_id: None,
+            ip: IpFragVersionSpecId::Ipv4 {
+                source: [1, 2, 3, 4],
+                destination: [5, 6, 7, 8],
+                identification: 0,
+            },
+            payload_ip_number: IpNumber::UDP,
+            channel_id: (),
+        };
+        let frag_id_1 = IpFragId {
+            outer_vlan_id: None,
+            inner_vlan_id: None,
+            ip: IpFragVersionSpecId::Ipv4 {
+                source: [1, 2, 3, 4],
+                destination: [5, 6, 7, 8],
+                identification: 1,
+            },
+            payload_ip_number: IpNumber::UDP,
+            channel_id: (),
+        };
+
+        let mut pool = IpDefragPool::<u32, ()>::new();
+
+        // packet timestamp 1
+        {
+            let pdata = build_packet(frag_id_0.clone(), 0, true, &[1, 2, 3, 4, 5, 6, 7, 8]);
+            let pslice = SlicedPacket::from_ethernet(&pdata).unwrap();
+            let v = pool.process_sliced_packet(&pslice, 1, ());
+            assert_eq!(Ok(None), v);
+        }
+        // packet timestamp 2
+        {
+            let pdata = build_packet(frag_id_1.clone(), 0, true, &[1, 2, 3, 4, 5, 6, 7, 8]);
+            let pslice = SlicedPacket::from_ethernet(&pdata).unwrap();
+            let v = pool.process_sliced_packet(&pslice, 2, ());
+            assert_eq!(Ok(None), v);
+        }
+
+        // check buffers are active
+        assert_eq!(pool.active.len(), 2);
+        assert_eq!(pool.finished_data_bufs.len(), 0);
+        assert_eq!(pool.finished_section_bufs.len(), 0);
+
+        // call retain without effect
+        pool.retain(|ts| *ts > 0);
+        assert_eq!(pool.active.len(), 2);
+        assert_eq!(pool.finished_data_bufs.len(), 0);
+        assert_eq!(pool.finished_section_bufs.len(), 0);
+
+        // call retain and delete timestamp 1
+        pool.retain(|ts| *ts > 1);
+        assert_eq!(pool.active.len(), 1);
+        assert_eq!(pool.finished_data_bufs.len(), 1);
+        assert_eq!(pool.finished_section_bufs.len(), 1);
+        assert_eq!(pool.active.iter().next().unwrap().0, &frag_id_1);
+    }
+}
diff --git a/src/defrag/ip_frag_id.rs b/src/defrag/ip_frag_id.rs
new file mode 100644
index 0000000..865d3f6
--- /dev/null
+++ b/src/defrag/ip_frag_id.rs
@@ -0,0 +1,24 @@
+use crate::{defrag::*, *};
+
+/// Values identifying a fragmented packet.
+#[derive(Debug, Clone, Hash, Eq, PartialEq)]
+pub struct IpFragId<CustomChannelId = ()>
+where
+    CustomChannelId: core::hash::Hash + Eq + PartialEq + Clone + Sized,
+{
+    /// First VLAN id of the fragmented packets.
+    pub outer_vlan_id: Option<VlanId>,
+
+    /// Second VLAN id of the fragmented packets.
+    pub inner_vlan_id: Option<VlanId>,
+
+    /// IP source & destination address & identifaction field.
+    pub ip: IpFragVersionSpecId,
+
+    /// IP number of the payload.
+    pub payload_ip_number: IpNumber,
+
+    /// Custom user defined channel identifier (can be used to differentiate packet
+    /// sources if the normal ethernet packets identifier are not enough).
+    pub channel_id: CustomChannelId,
+}
diff --git a/src/defrag/ip_frag_range.rs b/src/defrag/ip_frag_range.rs
new file mode 100644
index 0000000..eb9e56f
--- /dev/null
+++ b/src/defrag/ip_frag_range.rs
@@ -0,0 +1,108 @@
+/// Describing the range of reconstructed data.
+#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Ord, PartialOrd)]
+pub struct IpFragRange {
+    /// Offset of section
+    pub start: u16,
+    /// Offset + length of section
+    pub end: u16,
+}
+
+impl IpFragRange {
+    /// Return if the value is contained within the section.
+    fn is_value_connected(&self, value: u16) -> bool {
+        self.start <= value && self.end >= value
+    }
+
+    /// Combine both sections if possible.
+    pub fn merge(&self, other: IpFragRange) -> Option<IpFragRange> {
+        if self.is_value_connected(other.start)
+            || self.is_value_connected(other.end)
+            || other.is_value_connected(self.start)
+            || other.is_value_connected(self.end)
+        {
+            Some(IpFragRange {
+                start: core::cmp::min(self.start, other.start),
+                end: core::cmp::max(self.end, other.end),
+            })
+        } else {
+            None
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use alloc::format;
+
+    #[test]
+    fn debug_clone_eq() {
+        let section = IpFragRange { start: 1, end: 2 };
+        let _ = format!("{:?}", section);
+        assert_eq!(section, section.clone());
+        assert_eq!(section.cmp(&section), core::cmp::Ordering::Equal);
+        assert_eq!(
+            section.partial_cmp(&section),
+            Some(core::cmp::Ordering::Equal)
+        );
+
+        use core::hash::{Hash, Hasher};
+        use std::collections::hash_map::DefaultHasher;
+        let h1 = {
+            let mut h = DefaultHasher::new();
+            section.hash(&mut h);
+            h.finish()
+        };
+        let h2 = {
+            let mut h = DefaultHasher::new();
+            section.clone().hash(&mut h);
+            h.finish()
+        };
+        assert_eq!(h1, h2);
+    }
+
+    #[test]
+    fn is_value_connected() {
+        let s = IpFragRange { start: 5, end: 9 };
+        assert_eq!(false, s.is_value_connected(3));
+        assert_eq!(false, s.is_value_connected(4));
+        assert!(s.is_value_connected(5));
+        assert!(s.is_value_connected(6));
+        assert!(s.is_value_connected(7));
+        assert!(s.is_value_connected(8));
+        assert!(s.is_value_connected(9));
+        assert_eq!(false, s.is_value_connected(10));
+        assert_eq!(false, s.is_value_connected(11));
+    }
+
+    #[test]
+    fn merge() {
+        let tests = [
+            ((0, 1), (1, 2), Some((0, 2))),
+            ((0, 1), (2, 3), None),
+            ((3, 7), (1, 2), None),
+            ((3, 7), (1, 3), Some((1, 7))),
+            ((3, 7), (1, 4), Some((1, 7))),
+            ((3, 7), (1, 5), Some((1, 7))),
+            ((3, 7), (1, 6), Some((1, 7))),
+            ((3, 7), (1, 7), Some((1, 7))),
+            ((3, 7), (1, 8), Some((1, 8))),
+        ];
+        for t in tests {
+            let a = IpFragRange {
+                start: t.0 .0,
+                end: t.0 .1,
+            };
+            let b = IpFragRange {
+                start: t.1 .0,
+                end: t.1 .1,
+            };
+            let expected = t.2.map(|v| IpFragRange {
+                start: v.0,
+                end: v.1,
+            });
+            assert_eq!(a.merge(b), expected);
+            assert_eq!(b.merge(a), expected);
+        }
+    }
+}
diff --git a/src/defrag/ip_frag_version_spec_id.rs b/src/defrag/ip_frag_version_spec_id.rs
new file mode 100644
index 0000000..0372904
--- /dev/null
+++ b/src/defrag/ip_frag_version_spec_id.rs
@@ -0,0 +1,16 @@
+/// IPv4 & IPv6 specific fragment identifying information.
+#[derive(Debug, Clone, Hash, Eq, PartialEq)]
+pub enum IpFragVersionSpecId {
+    /// IPv4 specific data.
+    Ipv4 {
+        source: [u8; 4],
+        destination: [u8; 4],
+        identification: u16,
+    },
+    /// IPv6 specific data.
+    Ipv6 {
+        source: [u8; 16],
+        destination: [u8; 16],
+        identification: u32,
+    },
+}
diff --git a/src/defrag/mod.rs b/src/defrag/mod.rs
new file mode 100644
index 0000000..2285f90
--- /dev/null
+++ b/src/defrag/mod.rs
@@ -0,0 +1,26 @@
+mod ip_defrag_buf;
+pub use ip_defrag_buf::*;
+
+mod ip_defrag_error;
+pub use ip_defrag_error::*;
+
+mod ip_defrag_payload_vec;
+pub use ip_defrag_payload_vec::*;
+
+mod ip_defrag_pool;
+pub use ip_defrag_pool::*;
+
+mod ip_frag_id;
+pub use ip_frag_id::*;
+
+mod ip_frag_range;
+pub use ip_frag_range::*;
+
+mod ip_frag_version_spec_id;
+pub use ip_frag_version_spec_id::*;
+
+/// Maximum length of a defragmented packet as [`u16`].
+pub const MAX_IP_DEFRAG_LEN_U16: u16 = u16::MAX;
+
+/// Maximum length of a defragmented packet as [`usize`].
+pub const MAX_IP_DEFRAG_LEN: usize = MAX_IP_DEFRAG_LEN_U16 as usize;
diff --git a/src/err/double_vlan/header_error.rs b/src/err/double_vlan/header_error.rs
new file mode 100644
index 0000000..f38b3d9
--- /dev/null
+++ b/src/err/double_vlan/header_error.rs
@@ -0,0 +1,95 @@
+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/src/err/double_vlan/header_read_error.rs b/src/err/double_vlan/header_read_error.rs
new file mode 100644
index 0000000..e24c243
--- /dev/null
+++ b/src/err/double_vlan/header_read_error.rs
@@ -0,0 +1,151 @@
+use super::HeaderError;
+
+/// Error when decoding two VLAN headers via a `std::io::Read` source.
+///
+/// Requires crate feature `std`.
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+#[derive(Debug)]
+pub enum HeaderReadError {
+    /// IO error was encountered while reading header.
+    Io(std::io::Error),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl HeaderReadError {
+    /// Returns the `std::io::Error` value if the `HeaderReadError` is `Io`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn io_error(self) -> Option<std::io::Error> {
+        use HeaderReadError::*;
+        match self {
+            Io(value) => Some(value),
+            _ => None,
+        }
+    }
+
+    /// Returns the `err::double_vlan::HeaderError` value if the `HeaderReadError` is `Content`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn content_error(self) -> Option<HeaderError> {
+        use HeaderReadError::*;
+        match self {
+            Content(value) => Some(value),
+            _ => None,
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+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),
+            Content(value) => value.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderReadError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderReadError::*;
+        match self {
+            Io(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(all(test, feature = "std"))]
+mod test {
+    use super::{HeaderReadError::*, *};
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::NonVlanEtherType {
+            unexpected_ether_type: 1.into(),
+        };
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn fmt() {
+        {
+            let err = std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            );
+            assert_eq!(
+                format!("Double VLAN Header IO Error: {}", err),
+                format!("{}", Io(err))
+            );
+        }
+        {
+            let err = HeaderError::NonVlanEtherType {
+                unexpected_ether_type: 1.into(),
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[test]
+    fn source() {
+        use std::error::Error;
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .source()
+        .is_some());
+        assert!(Content(HeaderError::NonVlanEtherType {
+            unexpected_ether_type: 1.into()
+        })
+        .source()
+        .is_some());
+    }
+
+    #[test]
+    fn io_error() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .io_error()
+        .is_some());
+        assert!(Content(HeaderError::NonVlanEtherType {
+            unexpected_ether_type: 1.into()
+        })
+        .io_error()
+        .is_none());
+    }
+
+    #[test]
+    fn content_error() {
+        assert_eq!(
+            None,
+            Io(std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            ))
+            .content_error()
+        );
+        {
+            let err = HeaderError::NonVlanEtherType {
+                unexpected_ether_type: 1.into(),
+            };
+            assert_eq!(Some(err.clone()), Content(err.clone()).content_error());
+        }
+    }
+}
diff --git a/src/err/double_vlan/header_slice_error.rs b/src/err/double_vlan/header_slice_error.rs
new file mode 100644
index 0000000..7b78eea
--- /dev/null
+++ b/src/err/double_vlan/header_slice_error.rs
@@ -0,0 +1,158 @@
+use super::HeaderError;
+use crate::err::LenError;
+
+/// Error when decoding a double VLAN header from a slice.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeaderSliceError {
+    /// Error when an length error is encountered (e.g. unexpected
+    /// end of slice).
+    Len(LenError),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+impl HeaderSliceError {
+    /// Adds an offset value to all slice length related fields.
+    #[inline]
+    pub const fn add_slice_offset(self, offset: usize) -> Self {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => Len(err.add_offset(offset)),
+            Content(err) => Content(err),
+        }
+    }
+}
+
+impl core::fmt::Display for HeaderSliceError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => err.fmt(f),
+            Content(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderSliceError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{HeaderSliceError::*, *};
+    use crate::{err::Layer, LenSource};
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn add_slice_offset() {
+        assert_eq!(
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3
+            })
+            .add_slice_offset(200),
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 203
+            })
+        );
+        assert_eq!(
+            Content(HeaderError::NonVlanEtherType {
+                unexpected_ether_type: 1.into()
+            })
+            .add_slice_offset(200),
+            Content(HeaderError::NonVlanEtherType {
+                unexpected_ether_type: 1.into()
+            })
+        );
+    }
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::NonVlanEtherType {
+            unexpected_ether_type: 1.into(),
+        };
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Content(HeaderError::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() {
+        {
+            let err = LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Len(err)));
+        }
+        {
+            let err = HeaderError::NonVlanEtherType {
+                unexpected_ether_type: 1.into(),
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Len(LenError {
+            required_len: 1,
+            layer: Layer::Icmpv4,
+            len: 2,
+            len_source: LenSource::Slice,
+            layer_start_offset: 3
+        })
+        .source()
+        .is_some());
+        assert!(Content(HeaderError::NonVlanEtherType {
+            unexpected_ether_type: 1.into()
+        })
+        .source()
+        .is_some());
+    }
+}
diff --git a/src/err/double_vlan/mod.rs b/src/err/double_vlan/mod.rs
new file mode 100644
index 0000000..bf4f0e8
--- /dev/null
+++ b/src/err/double_vlan/mod.rs
@@ -0,0 +1,10 @@
+mod header_error;
+pub use header_error::*;
+
+#[cfg(feature = "std")]
+mod header_read_error;
+#[cfg(feature = "std")]
+pub use header_read_error::*;
+
+mod header_slice_error;
+pub use header_slice_error::*;
diff --git a/src/err/from_slice_error.rs b/src/err/from_slice_error.rs
new file mode 100644
index 0000000..e879edf
--- /dev/null
+++ b/src/err/from_slice_error.rs
@@ -0,0 +1,876 @@
+use super::*;
+
+/// "Catch all" error for all `from_slice` errors (supports automatic conversion from all
+/// other slice errors).
+///
+/// This type aggregates all errors that can be caused by decoding from a slice.
+///
+/// This type can be used as a "catch all" type for errors caused by `from_slice` functions
+/// as all errors from these functions can be converted into this type.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum FromSliceError {
+    /// Error when parsing had to be aborted because of a length error (usually
+    /// not enough data being available).
+    Len(LenError),
+
+    /// Error when decoding an Linux SLL header.
+    LinuxSll(linux_sll::HeaderError),
+
+    /// Error while parsing a double vlan header.
+    DoubleVlan(double_vlan::HeaderError),
+
+    /// Error while parsing a IP header.
+    Ip(ip::HeaderError),
+
+    /// Error while parsing a IP authentication header.
+    IpAuth(ip_auth::HeaderError),
+
+    /// Error while parsing a IPv4 header.
+    Ipv4(ipv4::HeaderError),
+
+    /// Error while parsing a IPv6 header.
+    Ipv6(ipv6::HeaderError),
+
+    /// Error while parsing a IPv6 extension header.
+    Ipv6Exts(ipv6_exts::HeaderError),
+
+    /// Error while parsing a TCP extension header.
+    Tcp(tcp::HeaderError),
+}
+
+impl FromSliceError {
+    pub fn len(&self) -> Option<&LenError> {
+        match self {
+            FromSliceError::Len(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn linux_sll(&self) -> Option<&linux_sll::HeaderError> {
+        match self {
+            FromSliceError::LinuxSll(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn double_vlan(&self) -> Option<&double_vlan::HeaderError> {
+        match self {
+            FromSliceError::DoubleVlan(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn ip(&self) -> Option<&ip::HeaderError> {
+        match self {
+            FromSliceError::Ip(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn ip_auth(&self) -> Option<&ip_auth::HeaderError> {
+        match self {
+            FromSliceError::IpAuth(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn ipv4(&self) -> Option<&ipv4::HeaderError> {
+        match self {
+            FromSliceError::Ipv4(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn ipv6(&self) -> Option<&ipv6::HeaderError> {
+        match self {
+            FromSliceError::Ipv6(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn ipv6_exts(&self) -> Option<&ipv6_exts::HeaderError> {
+        match self {
+            FromSliceError::Ipv6Exts(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn tcp(&self) -> Option<&tcp::HeaderError> {
+        match self {
+            FromSliceError::Tcp(err) => Some(err),
+            _ => None,
+        }
+    }
+}
+
+impl core::fmt::Display for FromSliceError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use FromSliceError::*;
+        match self {
+            Len(err) => err.fmt(f),
+            LinuxSll(err) => err.fmt(f),
+            DoubleVlan(err) => err.fmt(f),
+            Ip(err) => err.fmt(f),
+            IpAuth(err) => err.fmt(f),
+            Ipv4(err) => err.fmt(f),
+            Ipv6(err) => err.fmt(f),
+            Ipv6Exts(err) => err.fmt(f),
+            Tcp(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for FromSliceError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        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 error conversions
+
+impl From<LenError> for FromSliceError {
+    fn from(value: LenError) -> Self {
+        FromSliceError::Len(value)
+    }
+}
+
+// linux sll conversions
+
+impl From<linux_sll::HeaderError> for FromSliceError {
+    fn from(value: linux_sll::HeaderError) -> Self {
+        FromSliceError::LinuxSll(value)
+    }
+}
+
+impl From<linux_sll::HeaderSliceError> for FromSliceError {
+    fn from(value: linux_sll::HeaderSliceError) -> Self {
+        use linux_sll::HeaderSliceError::*;
+        match value {
+            Len(err) => FromSliceError::Len(err),
+            Content(err) => FromSliceError::LinuxSll(err),
+        }
+    }
+}
+
+// double vlan error conversions
+
+impl From<double_vlan::HeaderError> for FromSliceError {
+    fn from(value: double_vlan::HeaderError) -> Self {
+        FromSliceError::DoubleVlan(value)
+    }
+}
+
+impl From<double_vlan::HeaderSliceError> 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<ip::HeaderError> for FromSliceError {
+    fn from(value: ip::HeaderError) -> Self {
+        FromSliceError::Ip(value)
+    }
+}
+
+impl From<ip::HeadersError> for FromSliceError {
+    fn from(value: ip::HeadersError) -> Self {
+        match value {
+            ip::HeadersError::Ip(err) => FromSliceError::Ip(err),
+            ip::HeadersError::Ipv4Ext(err) => FromSliceError::IpAuth(err),
+            ip::HeadersError::Ipv6Ext(err) => FromSliceError::Ipv6Exts(err),
+        }
+    }
+}
+
+impl From<ip::HeadersSliceError> for FromSliceError {
+    fn from(value: ip::HeadersSliceError) -> Self {
+        use ip::HeadersSliceError::*;
+        match value {
+            Len(err) => FromSliceError::Len(err),
+            Content(err) => err.into(),
+        }
+    }
+}
+
+impl From<ip::SliceError> for FromSliceError {
+    fn from(value: ip::SliceError) -> Self {
+        use ip::SliceError::*;
+        match value {
+            Len(err) => FromSliceError::Len(err),
+            IpHeaders(err) => err.into(),
+        }
+    }
+}
+
+// ip auth error conversions
+
+impl From<ip_auth::HeaderError> for FromSliceError {
+    fn from(value: ip_auth::HeaderError) -> Self {
+        FromSliceError::IpAuth(value)
+    }
+}
+
+impl From<ip_auth::HeaderSliceError> for FromSliceError {
+    fn from(value: ip_auth::HeaderSliceError) -> Self {
+        use ip_auth::HeaderSliceError::*;
+        match value {
+            Len(err) => FromSliceError::Len(err),
+            Content(err) => FromSliceError::IpAuth(err),
+        }
+    }
+}
+
+// ipv4 error conversions
+
+impl From<ipv4::HeaderError> for FromSliceError {
+    fn from(value: ipv4::HeaderError) -> Self {
+        FromSliceError::Ipv4(value)
+    }
+}
+
+impl From<ipv4::HeaderSliceError> for FromSliceError {
+    fn from(value: ipv4::HeaderSliceError) -> Self {
+        use ipv4::HeaderSliceError::*;
+        match value {
+            Len(err) => FromSliceError::Len(err),
+            Content(err) => FromSliceError::Ipv4(err),
+        }
+    }
+}
+
+impl From<ipv4::SliceError> for FromSliceError {
+    fn from(value: ipv4::SliceError) -> Self {
+        use ipv4::SliceError::*;
+        match value {
+            Len(err) => FromSliceError::Len(err),
+            Header(err) => FromSliceError::Ipv4(err),
+            Exts(err) => FromSliceError::IpAuth(err),
+        }
+    }
+}
+
+// ipv6 error conversions
+
+impl From<ipv6::HeaderError> for FromSliceError {
+    fn from(value: ipv6::HeaderError) -> Self {
+        FromSliceError::Ipv6(value)
+    }
+}
+
+impl From<ipv6::HeaderSliceError> for FromSliceError {
+    fn from(value: ipv6::HeaderSliceError) -> Self {
+        use ipv6::HeaderSliceError::*;
+        match value {
+            Len(err) => FromSliceError::Len(err),
+            Content(err) => FromSliceError::Ipv6(err),
+        }
+    }
+}
+
+impl From<ipv6::SliceError> for FromSliceError {
+    fn from(value: ipv6::SliceError) -> Self {
+        use ipv6::SliceError::*;
+        match value {
+            Len(err) => FromSliceError::Len(err),
+            Header(err) => FromSliceError::Ipv6(err),
+            Exts(err) => FromSliceError::Ipv6Exts(err),
+        }
+    }
+}
+
+// ipv6 exts error conversions
+
+impl From<ipv6_exts::HeaderError> for FromSliceError {
+    fn from(value: ipv6_exts::HeaderError) -> Self {
+        FromSliceError::Ipv6Exts(value)
+    }
+}
+
+impl From<ipv6_exts::HeaderSliceError> for FromSliceError {
+    fn from(value: ipv6_exts::HeaderSliceError) -> Self {
+        use ipv6_exts::HeaderSliceError::*;
+        match value {
+            Len(err) => FromSliceError::Len(err),
+            Content(err) => FromSliceError::Ipv6Exts(err),
+        }
+    }
+}
+
+// packet error conversions
+
+impl From<packet::SliceError> for FromSliceError {
+    fn from(value: packet::SliceError) -> Self {
+        use packet::SliceError::*;
+        match value {
+            Len(err) => FromSliceError::Len(err),
+            LinuxSll(err) => FromSliceError::LinuxSll(err),
+            Ip(err) => FromSliceError::Ip(err),
+            Ipv4(err) => FromSliceError::Ipv4(err),
+            Ipv6(err) => FromSliceError::Ipv6(err),
+            Ipv4Exts(err) => FromSliceError::IpAuth(err),
+            Ipv6Exts(err) => FromSliceError::Ipv6Exts(err),
+            Tcp(err) => FromSliceError::Tcp(err),
+        }
+    }
+}
+
+// tcp error conversions
+
+impl From<tcp::HeaderError> for FromSliceError {
+    fn from(value: tcp::HeaderError) -> Self {
+        FromSliceError::Tcp(value)
+    }
+}
+
+impl From<tcp::HeaderSliceError> for FromSliceError {
+    fn from(value: tcp::HeaderSliceError) -> Self {
+        use tcp::HeaderSliceError::*;
+        match value {
+            Len(err) => FromSliceError::Len(err),
+            Content(err) => FromSliceError::Tcp(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::{ArpHardwareId, EtherType, LenSource};
+
+    use super::{FromSliceError::*, *};
+    use core::hash::{Hash, Hasher};
+    use std::collections::hash_map::DefaultHasher;
+    use std::error::Error;
+    use std::format;
+
+    #[test]
+    fn clone_eq_hash() {
+        let value = Len(LenError {
+            required_len: 0,
+            len: 0,
+            len_source: LenSource::Slice,
+            layer: Layer::Icmpv4,
+            layer_start_offset: 0,
+        });
+        assert_eq!(value, value.clone());
+        let h1 = {
+            let mut h = DefaultHasher::new();
+            value.hash(&mut h);
+            h.finish()
+        };
+        let h2 = {
+            let mut h = DefaultHasher::new();
+            value.clone().hash(&mut h);
+            h.finish()
+        };
+        assert_eq!(h1, h2);
+    }
+
+    #[test]
+    fn debug_source() {
+        let test_values: [(&str, FromSliceError); 8] = [
+            (
+                "Len",
+                Len(LenError {
+                    required_len: 0,
+                    len: 0,
+                    len_source: LenSource::Slice,
+                    layer: Layer::Icmpv4,
+                    layer_start_offset: 0,
+                }),
+            ),
+            (
+                "DoubleVlan",
+                DoubleVlan(double_vlan::HeaderError::NonVlanEtherType {
+                    unexpected_ether_type: EtherType(123),
+                }),
+            ),
+            (
+                "Ip",
+                Ip(ip::HeaderError::UnsupportedIpVersion {
+                    version_number: 123,
+                }),
+            ),
+            ("IpAuth", IpAuth(ip_auth::HeaderError::ZeroPayloadLen)),
+            (
+                "Ipv4",
+                Ipv4(ipv4::HeaderError::UnexpectedVersion { version_number: 1 }),
+            ),
+            (
+                "Ipv6",
+                Ipv6(ipv6::HeaderError::UnexpectedVersion { version_number: 1 }),
+            ),
+            (
+                "Ipv6Exts",
+                Ipv6Exts(ipv6_exts::HeaderError::HopByHopNotAtStart),
+            ),
+            (
+                "Tcp",
+                Tcp(tcp::HeaderError::DataOffsetTooSmall { data_offset: 1 }),
+            ),
+        ];
+        for (prefix, value) in &test_values {
+            // display
+            assert_eq!(
+                format!("{:?}", value),
+                format!("{}({:?})", prefix, value.source().unwrap())
+            );
+        }
+    }
+
+    #[test]
+    fn display_source() {
+        let test_values: [FromSliceError; 9] = [
+            Len(LenError {
+                required_len: 0,
+                len: 0,
+                len_source: LenSource::Slice,
+                layer: Layer::Icmpv4,
+                layer_start_offset: 0,
+            }),
+            LinuxSll(linux_sll::HeaderError::UnsupportedArpHardwareId {
+                arp_hardware_type: ArpHardwareId::ETHER,
+            }),
+            DoubleVlan(double_vlan::HeaderError::NonVlanEtherType {
+                unexpected_ether_type: EtherType(123),
+            }),
+            Ip(ip::HeaderError::UnsupportedIpVersion {
+                version_number: 123,
+            }),
+            IpAuth(ip_auth::HeaderError::ZeroPayloadLen),
+            Ipv4(ipv4::HeaderError::UnexpectedVersion { version_number: 1 }),
+            Ipv6(ipv6::HeaderError::UnexpectedVersion { version_number: 1 }),
+            Ipv6Exts(ipv6_exts::HeaderError::HopByHopNotAtStart),
+            Tcp(tcp::HeaderError::DataOffsetTooSmall { data_offset: 1 }),
+        ];
+        for value in &test_values {
+            // display
+            assert_eq!(format!("{}", value), format!("{}", value.source().unwrap()));
+        }
+    }
+
+    #[test]
+    fn accessors() {
+        use FromSliceError::*;
+        let len_error = || LenError {
+            required_len: 0,
+            len: 0,
+            len_source: LenSource::Slice,
+            layer: Layer::Icmpv4,
+            layer_start_offset: 0,
+        };
+        let linux_sll_error = || linux_sll::HeaderError::UnsupportedArpHardwareId {
+            arp_hardware_type: ArpHardwareId::ETHER,
+        };
+        let double_vlan_error = || double_vlan::HeaderError::NonVlanEtherType {
+            unexpected_ether_type: EtherType(1),
+        };
+        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 };
+        let ip_auth_error = || ip_auth::HeaderError::ZeroPayloadLen;
+        let ipv6_exts_error = || ipv6_exts::HeaderError::HopByHopNotAtStart;
+        let tcp_error = || tcp::HeaderError::DataOffsetTooSmall { data_offset: 1 };
+
+        // len
+        assert_eq!(Len(len_error()).len(), Some(&len_error()));
+        assert_eq!(Ipv4(ipv4_error()).len(), None);
+
+        // linux_sll
+        assert_eq!(
+            LinuxSll(linux_sll_error()).linux_sll(),
+            Some(&linux_sll_error())
+        );
+        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);
+
+        // ip
+        assert_eq!(Ip(ip_error()).ip(), Some(&ip_error()));
+        assert_eq!(Ipv4(ipv4_error()).ip(), None);
+
+        // ip_auth
+        assert_eq!(IpAuth(ip_auth_error()).ip_auth(), Some(&ip_auth_error()));
+        assert_eq!(Ipv4(ipv4_error()).ip_auth(), None);
+
+        // ipv4
+        assert_eq!(Ipv4(ipv4_error()).ipv4(), Some(&ipv4_error()));
+        assert_eq!(IpAuth(ip_auth_error()).ipv4(), None);
+
+        // ipv6
+        assert_eq!(Ipv6(ipv6_error()).ipv6(), Some(&ipv6_error()));
+        assert_eq!(IpAuth(ip_auth_error()).ipv6(), None);
+
+        // ipv6_exts
+        assert_eq!(
+            Ipv6Exts(ipv6_exts_error()).ipv6_exts(),
+            Some(&ipv6_exts_error())
+        );
+        assert_eq!(IpAuth(ip_auth_error()).ipv6_exts(), None);
+
+        // tcp
+        assert_eq!(Tcp(tcp_error()).tcp(), Some(&tcp_error()));
+        assert_eq!(IpAuth(ip_auth_error()).tcp(), None);
+    }
+
+    #[test]
+    fn from() {
+        let len_error = || -> LenError {
+            LenError {
+                required_len: 0,
+                len: 0,
+                len_source: LenSource::Slice,
+                layer: Layer::Icmpv4,
+                layer_start_offset: 0,
+            }
+        };
+
+        // len
+        assert_eq!(
+            &len_error(),
+            FromSliceError::from(len_error()).len().unwrap()
+        );
+
+        // linux sll
+        {
+            let header_error = || linux_sll::HeaderError::UnsupportedArpHardwareId {
+                arp_hardware_type: ArpHardwareId::ETHER,
+            };
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(header_error()).linux_sll().unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(linux_sll::HeaderSliceError::Content(header_error()))
+                    .linux_sll()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                FromSliceError::from(linux_sll::HeaderSliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(linux_sll::HeaderSliceError::Content(header_error()))
+                    .linux_sll()
+                    .unwrap()
+            );
+        }
+
+        // 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,
+            };
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(header_error()).ip().unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(ip::HeadersSliceError::Content(ip::HeadersError::Ip(
+                    header_error()
+                )))
+                .ip()
+                .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                FromSliceError::from(ip::HeadersSliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                FromSliceError::from(ip::SliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(ip::SliceError::IpHeaders(ip::HeadersError::Ip(
+                    header_error()
+                )))
+                .ip()
+                .unwrap()
+            );
+        }
+
+        // ip auth errors
+        {
+            let header_error = || ip_auth::HeaderError::ZeroPayloadLen;
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(header_error()).ip_auth().unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(ip_auth::HeaderSliceError::Content(header_error()))
+                    .ip_auth()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                FromSliceError::from(ip_auth::HeaderSliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(ip_auth::HeaderSliceError::Content(header_error()))
+                    .ip_auth()
+                    .unwrap()
+            );
+        }
+
+        // ipv4 errors
+        {
+            let header_error = || ipv4::HeaderError::UnexpectedVersion {
+                version_number: 123,
+            };
+            let exts_error = || ip_auth::HeaderError::ZeroPayloadLen;
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(header_error()).ipv4().unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(ipv4::HeaderSliceError::Content(header_error()))
+                    .ipv4()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                FromSliceError::from(ipv4::HeaderSliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(ipv4::HeaderSliceError::Content(header_error()))
+                    .ipv4()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                FromSliceError::from(ipv4::SliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(ipv4::SliceError::Header(header_error()))
+                    .ipv4()
+                    .unwrap()
+            );
+            assert_eq!(
+                &exts_error(),
+                FromSliceError::from(ipv4::SliceError::Exts(exts_error()))
+                    .ip_auth()
+                    .unwrap()
+            );
+        }
+
+        // ipv6 errors
+        {
+            let header_error = || ipv6::HeaderError::UnexpectedVersion {
+                version_number: 123,
+            };
+            let exts_error = || ipv6_exts::HeaderError::HopByHopNotAtStart;
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(header_error()).ipv6().unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(ipv6::HeaderSliceError::Content(header_error()))
+                    .ipv6()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                FromSliceError::from(ipv6::HeaderSliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(ipv6::HeaderSliceError::Content(header_error()))
+                    .ipv6()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                FromSliceError::from(ipv6::SliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(ipv6::SliceError::Header(header_error()))
+                    .ipv6()
+                    .unwrap()
+            );
+            assert_eq!(
+                &exts_error(),
+                FromSliceError::from(ipv6::SliceError::Exts(exts_error()))
+                    .ipv6_exts()
+                    .unwrap()
+            );
+        }
+
+        // ipv6 exts errors
+        {
+            let header_error = || ipv6_exts::HeaderError::HopByHopNotAtStart;
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(header_error()).ipv6_exts().unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(ipv6_exts::HeaderSliceError::Content(header_error()))
+                    .ipv6_exts()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                FromSliceError::from(ipv6_exts::HeaderSliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(ipv6_exts::HeaderSliceError::Content(header_error()))
+                    .ipv6_exts()
+                    .unwrap()
+            );
+        }
+
+        // packet error
+        {
+            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 };
+            let ip_auth_error = || ip_auth::HeaderError::ZeroPayloadLen;
+            let ipv6_exts_error = || ipv6_exts::HeaderError::HopByHopNotAtStart;
+            let tcp_error = || tcp::HeaderError::DataOffsetTooSmall { data_offset: 1 };
+
+            // SliceError
+            assert_eq!(
+                &len_error(),
+                FromSliceError::from(packet::SliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &ip_error(),
+                FromSliceError::from(packet::SliceError::Ip(ip_error()))
+                    .ip()
+                    .unwrap()
+            );
+            assert_eq!(
+                &ipv4_error(),
+                FromSliceError::from(packet::SliceError::Ipv4(ipv4_error()))
+                    .ipv4()
+                    .unwrap()
+            );
+            assert_eq!(
+                &ipv6_error(),
+                FromSliceError::from(packet::SliceError::Ipv6(ipv6_error()))
+                    .ipv6()
+                    .unwrap()
+            );
+            assert_eq!(
+                &ip_auth_error(),
+                FromSliceError::from(packet::SliceError::Ipv4Exts(ip_auth_error()))
+                    .ip_auth()
+                    .unwrap()
+            );
+            assert_eq!(
+                &ipv6_exts_error(),
+                FromSliceError::from(packet::SliceError::Ipv6Exts(ipv6_exts_error()))
+                    .ipv6_exts()
+                    .unwrap()
+            );
+            assert_eq!(
+                &tcp_error(),
+                FromSliceError::from(packet::SliceError::Tcp(tcp_error()))
+                    .tcp()
+                    .unwrap()
+            );
+        }
+
+        // tcp errors
+        {
+            let header_error = || tcp::HeaderError::DataOffsetTooSmall { data_offset: 1 };
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(header_error()).tcp().unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(tcp::HeaderSliceError::Content(header_error()))
+                    .tcp()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                FromSliceError::from(tcp::HeaderSliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                FromSliceError::from(tcp::HeaderSliceError::Content(header_error()))
+                    .tcp()
+                    .unwrap()
+            );
+        }
+    }
+} // mod tests
diff --git a/src/err/io/limited_read_error.rs b/src/err/io/limited_read_error.rs
new file mode 100644
index 0000000..196cbec
--- /dev/null
+++ b/src/err/io/limited_read_error.rs
@@ -0,0 +1,169 @@
+use crate::err::LenError;
+
+/// Error that can occur when reading from a [`crate::io::LimitedReader`]
+#[derive(Debug)]
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub enum LimitedReadError {
+    /// IO error was encountered while reading header or
+    /// expected packet contents.
+    Io(std::io::Error),
+
+    /// Error when parsing had to be aborted because a
+    /// length limit specified by an upper layer has been
+    /// exceeded.
+    Len(LenError),
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl LimitedReadError {
+    /// Returns the `std::io::Error` value if the `LimitedReadError` is `Io`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn io(self) -> Option<std::io::Error> {
+        use LimitedReadError::*;
+        match self {
+            Io(err) => Some(err),
+            _ => None,
+        }
+    }
+
+    /// Returns the `err::LenError` value if it is of value `Len`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn len(self) -> Option<LenError> {
+        use LimitedReadError::*;
+        match self {
+            Len(err) => Some(err),
+            _ => None,
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl core::fmt::Display for LimitedReadError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use LimitedReadError::*;
+        match self {
+            Io(err) => err.fmt(f),
+            Len(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for LimitedReadError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use LimitedReadError::*;
+        match self {
+            Io(err) => Some(err),
+            Len(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(all(test, feature = "std"))]
+mod test {
+    use super::{LimitedReadError::*, *};
+    use crate::{err::Layer, LenSource};
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        let err = LenError {
+            required_len: 2,
+            len: 1,
+            len_source: LenSource::Slice,
+            layer: Layer::Icmpv4,
+            layer_start_offset: 3,
+        };
+        assert_eq!(format!("Len({:?})", err.clone()), format!("{:?}", Len(err)));
+    }
+
+    #[test]
+    fn fmt() {
+        {
+            let err = std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            );
+            assert_eq!(format!("{}", err), format!("{}", Io(err)));
+        }
+        {
+            let err = LenError {
+                required_len: 2,
+                len: 1,
+                len_source: LenSource::Slice,
+                layer: Layer::Icmpv4,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Len(err.clone())));
+        }
+    }
+
+    #[test]
+    fn source() {
+        use std::error::Error;
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .source()
+        .is_some());
+        {
+            let err = LenError {
+                required_len: 2,
+                len: 1,
+                len_source: LenSource::Slice,
+                layer: Layer::Icmpv4,
+                layer_start_offset: 3,
+            };
+            assert!(Len(err).source().is_some());
+        }
+    }
+
+    #[test]
+    fn io() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .io()
+        .is_some());
+        {
+            let err = LenError {
+                required_len: 2,
+                len: 1,
+                len_source: LenSource::Slice,
+                layer: Layer::Icmpv4,
+                layer_start_offset: 3,
+            };
+            assert!(Len(err).io().is_none());
+        }
+    }
+
+    #[test]
+    fn len() {
+        assert_eq!(
+            None,
+            Io(std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            ))
+            .len()
+        );
+        {
+            let err = LenError {
+                required_len: 2,
+                len: 1,
+                len_source: LenSource::Slice,
+                layer: Layer::Icmpv4,
+                layer_start_offset: 3,
+            };
+            assert_eq!(Some(err.clone()), Len(err.clone()).len());
+        }
+    }
+}
diff --git a/src/err/io/mod.rs b/src/err/io/mod.rs
new file mode 100644
index 0000000..6feed29
--- /dev/null
+++ b/src/err/io/mod.rs
@@ -0,0 +1,2 @@
+mod limited_read_error;
+pub use limited_read_error::*;
diff --git a/src/err/ip/header_error.rs b/src/err/ip/header_error.rs
new file mode 100644
index 0000000..b694177
--- /dev/null
+++ b/src/err/ip/header_error.rs
@@ -0,0 +1,100 @@
+#[cfg(feature = "std")]
+use crate::*;
+
+/// Error when decoding the IP header part of a message.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeaderError {
+    /// Error when the IP header version field is not equal to 4 or 6.
+    UnsupportedIpVersion {
+        /// The unexpected version number in the IP header.
+        version_number: u8,
+    },
+
+    /// Error when the ipv4 internet header length is smaller then the header itself (5).
+    Ipv4HeaderLengthSmallerThanHeader {
+        /// The internet header length that was too small.
+        ihl: u8,
+    },
+}
+
+impl core::fmt::Display for HeaderError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderError::*;
+        match self {
+            UnsupportedIpVersion { version_number } => write!(f, "IP Header Error: Encountered '{}' as IP version number in the IP header (only '4' or '6' are supported).", version_number),
+            Ipv4HeaderLengthSmallerThanHeader { ihl } => write!(f, "IPv4 Header Error: The 'internet header length' value '{}' present in the IPv4 header is smaller than the minimum size of an IPv4 header. The minimum allowed value is '5'.", ihl),
+        }
+    }
+}
+
+#[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)> {
+        use HeaderError::*;
+        match self {
+            UnsupportedIpVersion { version_number: _ } => None,
+            Ipv4HeaderLengthSmallerThanHeader { ihl: _ } => 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!(
+            "UnsupportedIpVersion { version_number: 6 }",
+            format!("{:?}", UnsupportedIpVersion { version_number: 6 })
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = HeaderError::UnsupportedIpVersion { version_number: 6 };
+        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!(
+            "IP Header Error: Encountered '1' as IP version number in the IP header (only '4' or '6' are supported).",
+            format!("{}", UnsupportedIpVersion{ version_number: 1 })
+        );
+        assert_eq!(
+            "IPv4 Header Error: The 'internet header length' value '2' present in the IPv4 header is smaller than the minimum size of an IPv4 header. The minimum allowed value is '5'.",
+            format!("{}", Ipv4HeaderLengthSmallerThanHeader{ ihl: 2 })
+        );
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        let values = [
+            UnsupportedIpVersion { version_number: 0 },
+            Ipv4HeaderLengthSmallerThanHeader { ihl: 0 },
+        ];
+        for v in values {
+            assert!(v.source().is_none());
+        }
+    }
+}
diff --git a/src/err/ip/headers_error.rs b/src/err/ip/headers_error.rs
new file mode 100644
index 0000000..507bcbd
--- /dev/null
+++ b/src/err/ip/headers_error.rs
@@ -0,0 +1,117 @@
+use crate::*;
+
+/// Error when decoding the IP header part of a message.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeadersError {
+    /// Error in the IPv4 or IPv6 header.
+    Ip(err::ip::HeaderError),
+
+    /// Error in the IPv4 extension headers (only authentication header).
+    Ipv4Ext(err::ip_auth::HeaderError),
+
+    /// Error in the IPv6 extension headers.
+    Ipv6Ext(err::ipv6_exts::HeaderError),
+}
+
+impl core::fmt::Display for HeadersError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeadersError::*;
+        match self {
+            Ip(err) => err.fmt(f),
+            Ipv4Ext(err) => err.fmt(f),
+            Ipv6Ext(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeadersError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeadersError::*;
+        match self {
+            Ip(err) => Some(err),
+            Ipv4Ext(err) => Some(err),
+            Ipv6Ext(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{super::HeaderError::*, HeadersError::*, *};
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        assert_eq!(
+            "Ip(UnsupportedIpVersion { version_number: 6 })",
+            format!("{:?}", Ip(UnsupportedIpVersion { version_number: 6 }))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Ip(UnsupportedIpVersion { version_number: 6 });
+        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!(
+            "IP Header Error: Encountered '1' as IP version number in the IP header (only '4' or '6' are supported).",
+            format!("{}", UnsupportedIpVersion{ version_number: 1 })
+        );
+        assert_eq!(
+            "IPv4 Header Error: The 'internet header length' value '2' present in the IPv4 header is smaller than the minimum size of an IPv4 header. The minimum allowed value is '5'.",
+            format!("{}", Ipv4HeaderLengthSmallerThanHeader{ ihl: 2 })
+        );
+        {
+            let err = err::ip_auth::HeaderError::ZeroPayloadLen;
+            assert_eq!(format!("{}", Ipv4Ext(err.clone())), format!("{}", err));
+        }
+        {
+            let err = err::ipv6_exts::HeaderError::HopByHopNotAtStart;
+            assert_eq!(format!("{}", Ipv6Ext(err.clone())), format!("{}", err));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        {
+            let values = [
+                UnsupportedIpVersion { version_number: 0 },
+                Ipv4HeaderLengthSmallerThanHeader { ihl: 0 },
+            ];
+            for v in values {
+                assert!(v.source().is_none());
+            }
+        }
+        {
+            let values = [
+                Ipv4Ext(err::ip_auth::HeaderError::ZeroPayloadLen),
+                Ipv6Ext(err::ipv6_exts::HeaderError::HopByHopNotAtStart),
+            ];
+            for v in values {
+                assert!(v.source().is_some());
+            }
+        }
+    }
+}
diff --git a/src/err/ip/headers_read_error.rs b/src/err/ip/headers_read_error.rs
new file mode 100644
index 0000000..9874ccc
--- /dev/null
+++ b/src/err/ip/headers_read_error.rs
@@ -0,0 +1,198 @@
+use super::HeadersError;
+use crate::err::LenError;
+
+/// Error when decoding an IP header via a `std::io::Read` source.
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+#[derive(Debug)]
+pub enum HeaderReadError {
+    /// IO error was encountered while reading header.
+    Io(std::io::Error),
+
+    /// Errors caused by conflicts with the lengths defined
+    /// in the headers (i.e. IPv4 length too small to read the
+    /// lower layer headers)
+    Len(LenError),
+
+    /// Error caused by the contents of the header.
+    Content(HeadersError),
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl HeaderReadError {
+    /// Returns the `std::io::Error` value if the `HeaderReadError` is `Io`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn io(self) -> Option<std::io::Error> {
+        use HeaderReadError::*;
+        match self {
+            Io(value) => Some(value),
+            _ => None,
+        }
+    }
+
+    /// Returns the `err::LenError` value if the `HeaderReadError` is `Len`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn len(self) -> Option<LenError> {
+        use HeaderReadError::*;
+        match self {
+            Len(value) => Some(value),
+            _ => None,
+        }
+    }
+
+    /// Returns the `err::ip::HeaderError` value if the `HeaderReadError` is `Content`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn content(self) -> Option<HeadersError> {
+        use HeaderReadError::*;
+        match self {
+            Content(value) => Some(value),
+            _ => None,
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+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, "IP Header IO Error: {}", err),
+            Len(err) => err.fmt(f),
+            Content(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderReadError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderReadError::*;
+        match self {
+            Io(err) => Some(err),
+            Len(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(all(test, feature = "std"))]
+mod test {
+    use super::{super::HeaderError::*, super::HeadersError::*, HeaderReadError::*, *};
+    use crate::{err::Layer, LenSource};
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        let err = HeadersError::Ip(UnsupportedIpVersion { version_number: 6 });
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn fmt() {
+        {
+            let err = std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            );
+            assert_eq!(
+                format!("IP Header IO Error: {}", err),
+                format!("{}", Io(err))
+            );
+        }
+        {
+            let err = LenError {
+                required_len: 2,
+                len: 1,
+                len_source: LenSource::Slice,
+                layer: Layer::Icmpv4,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", Len(err.clone())), format!("{}", err));
+        }
+        {
+            let err = Ip(UnsupportedIpVersion { version_number: 6 });
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[test]
+    fn source() {
+        use std::error::Error;
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .source()
+        .is_some());
+        assert!(Len(LenError {
+            required_len: 2,
+            len: 1,
+            len_source: LenSource::Slice,
+            layer: Layer::Icmpv4,
+            layer_start_offset: 3,
+        })
+        .source()
+        .is_some());
+        assert!(Content(Ip(UnsupportedIpVersion { version_number: 6 }))
+            .source()
+            .is_some());
+    }
+
+    #[test]
+    fn io() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .io()
+        .is_some());
+        assert!(Content(Ip(UnsupportedIpVersion { version_number: 6 }))
+            .io()
+            .is_none());
+    }
+
+    #[test]
+    fn len() {
+        {
+            let err = LenError {
+                required_len: 2,
+                len: 1,
+                len_source: LenSource::Slice,
+                layer: Layer::Icmpv4,
+                layer_start_offset: 3,
+            };
+            assert_eq!(Len(err.clone()).len(), Some(err));
+        }
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .len()
+        .is_none());
+    }
+
+    #[test]
+    fn content() {
+        assert_eq!(
+            None,
+            Io(std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            ))
+            .content()
+        );
+        {
+            let err = Ip(UnsupportedIpVersion { version_number: 6 });
+            assert_eq!(Some(err.clone()), Content(err.clone()).content());
+        }
+    }
+}
diff --git a/src/err/ip/headers_slice_error.rs b/src/err/ip/headers_slice_error.rs
new file mode 100644
index 0000000..1682b7f
--- /dev/null
+++ b/src/err/ip/headers_slice_error.rs
@@ -0,0 +1,150 @@
+use super::HeadersError;
+use crate::err::LenError;
+
+/// Error when decoding an IP header from a slice.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeadersSliceError {
+    /// Error when an length error is encountered (e.g. unexpected
+    /// end of slice).
+    Len(LenError),
+
+    /// Error caused by the contents of the header.
+    Content(HeadersError),
+}
+
+impl HeadersSliceError {
+    /// Adds an offset value to all slice length related fields.
+    #[inline]
+    pub const fn add_slice_offset(self, offset: usize) -> Self {
+        use HeadersSliceError::*;
+        match self {
+            Len(err) => Len(err.add_offset(offset)),
+            Content(err) => Content(err),
+        }
+    }
+}
+
+impl core::fmt::Display for HeadersSliceError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeadersSliceError::*;
+        match self {
+            Len(err) => err.fmt(f),
+            Content(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeadersSliceError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeadersSliceError::*;
+        match self {
+            Len(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{
+        super::{HeaderError::*, HeadersError::*},
+        HeadersSliceError::*,
+        *,
+    };
+    use crate::{err::Layer, LenSource};
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn add_slice_offset() {
+        use HeadersSliceError::*;
+        assert_eq!(
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3
+            })
+            .add_slice_offset(200),
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 203
+            })
+        );
+        assert_eq!(
+            Content(Ip(UnsupportedIpVersion { version_number: 1 })).add_slice_offset(200),
+            Content(Ip(UnsupportedIpVersion { version_number: 1 }))
+        );
+    }
+
+    #[test]
+    fn debug() {
+        let err = Ip(UnsupportedIpVersion { version_number: 6 });
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Content(Ip(UnsupportedIpVersion { version_number: 6 }));
+        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() {
+        {
+            let err = LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Len(err)));
+        }
+        {
+            let err = Ip(UnsupportedIpVersion { version_number: 6 });
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Len(LenError {
+            required_len: 1,
+            layer: Layer::Icmpv4,
+            len: 2,
+            len_source: LenSource::Slice,
+            layer_start_offset: 3
+        })
+        .source()
+        .is_some());
+        assert!(Content(Ip(UnsupportedIpVersion { version_number: 6 }))
+            .source()
+            .is_some());
+    }
+}
diff --git a/src/err/ip/headers_write_error.rs b/src/err/ip/headers_write_error.rs
new file mode 100644
index 0000000..f5eb914
--- /dev/null
+++ b/src/err/ip/headers_write_error.rs
@@ -0,0 +1,183 @@
+#[cfg(feature = "std")]
+use crate::err::{ipv4_exts, ipv6_exts};
+
+/// Error when writing IPv4 extension headers.
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+#[derive(Debug)]
+pub enum HeadersWriteError {
+    /// IO error encountered while writing.
+    Io(std::io::Error),
+    /// IPv4 extensions can not be serialized (e.g. order
+    /// is not determinable as headers are never referenced).
+    Ipv4Exts(ipv4_exts::ExtsWalkError),
+    /// IPv6 extensions can not be serialized (e.g. order
+    /// is not determinable as headers are never referenced).
+    Ipv6Exts(ipv6_exts::ExtsWalkError),
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl HeadersWriteError {
+    /// Returns a reference to the [`std::io::Error`] if the value is an `Io`.
+    pub fn io(&self) -> Option<&std::io::Error> {
+        match self {
+            HeadersWriteError::Io(err) => Some(err),
+            _ => None,
+        }
+    }
+
+    /// Returns a reference to the [`crate::err::ipv4_exts::ExtsWalkError`]
+    /// if the value is an `Ipv4Exts`.
+    pub fn ipv4_exts(&self) -> Option<&ipv4_exts::ExtsWalkError> {
+        match self {
+            HeadersWriteError::Ipv4Exts(err) => Some(err),
+            _ => None,
+        }
+    }
+
+    /// Returns a reference to the [`crate::err::ipv6_exts::ExtsWalkError`]
+    /// if the value is an `Ipv6Exts`.
+    pub fn ipv6_exts(&self) -> Option<&ipv6_exts::ExtsWalkError> {
+        match self {
+            HeadersWriteError::Ipv6Exts(err) => Some(err),
+            _ => None,
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl core::fmt::Display for HeadersWriteError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeadersWriteError::*;
+        match self {
+            Io(err) => err.fmt(f),
+            Ipv4Exts(err) => err.fmt(f),
+            Ipv6Exts(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeadersWriteError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeadersWriteError::*;
+        match self {
+            Io(ref err) => Some(err),
+            Ipv4Exts(ref err) => Some(err),
+            Ipv6Exts(ref err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{HeadersWriteError::*, *};
+    use crate::*;
+    use alloc::format;
+    use std::error::Error;
+
+    #[test]
+    fn io() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .io()
+        .is_some());
+        assert!(Ipv4Exts(ipv4_exts::ExtsWalkError::ExtNotReferenced {
+            missing_ext: IpNumber::AUTHENTICATION_HEADER,
+        })
+        .io()
+        .is_none());
+    }
+
+    #[test]
+    fn ipv4_exts() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .ipv4_exts()
+        .is_none());
+        {
+            let err = ipv4_exts::ExtsWalkError::ExtNotReferenced {
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            };
+            assert_eq!(Some(&err), Ipv4Exts(err.clone()).ipv4_exts());
+        }
+    }
+
+    #[test]
+    fn ipv6_exts() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .ipv6_exts()
+        .is_none());
+        {
+            let err = ipv6_exts::ExtsWalkError::ExtNotReferenced {
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            };
+            assert_eq!(Some(&err), Ipv6Exts(err.clone()).ipv6_exts());
+        }
+    }
+
+    #[test]
+    fn debug() {
+        let err = ipv6_exts::ExtsWalkError::ExtNotReferenced {
+            missing_ext: IpNumber::AUTHENTICATION_HEADER,
+        };
+        assert_eq!(
+            format!("Ipv6Exts({:?})", err.clone()),
+            format!("{:?}", Ipv6Exts(err))
+        );
+    }
+
+    #[test]
+    fn fmt() {
+        {
+            let err = std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            );
+            assert_eq!(format!("{}", err), format!("{}", Io(err)));
+        }
+        {
+            let err = ipv4_exts::ExtsWalkError::ExtNotReferenced {
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            };
+            assert_eq!(format!("{}", Ipv4Exts(err.clone())), format!("{}", err));
+        }
+        {
+            let err = ipv6_exts::ExtsWalkError::ExtNotReferenced {
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            };
+            assert_eq!(format!("{}", Ipv6Exts(err.clone())), format!("{}", err));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .source()
+        .is_some());
+        assert!(Ipv4Exts(ipv4_exts::ExtsWalkError::ExtNotReferenced {
+            missing_ext: IpNumber::AUTHENTICATION_HEADER,
+        })
+        .source()
+        .is_some());
+        assert!(Ipv6Exts(ipv6_exts::ExtsWalkError::ExtNotReferenced {
+            missing_ext: IpNumber::AUTHENTICATION_HEADER,
+        })
+        .source()
+        .is_some());
+    }
+}
diff --git a/src/err/ip/lax_header_slice_error.rs b/src/err/ip/lax_header_slice_error.rs
new file mode 100644
index 0000000..ed6bc2b
--- /dev/null
+++ b/src/err/ip/lax_header_slice_error.rs
@@ -0,0 +1,146 @@
+use super::HeaderError;
+use crate::err::LenError;
+
+/// Error when decoding an IP header from a slice.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum LaxHeaderSliceError {
+    /// Error when an length error is encountered (e.g. unexpected
+    /// end of slice).
+    Len(LenError),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+impl LaxHeaderSliceError {
+    /// Adds an offset value to all slice length related fields.
+    #[inline]
+    pub const fn add_slice_offset(self, offset: usize) -> Self {
+        use LaxHeaderSliceError::*;
+        match self {
+            Len(err) => Len(err.add_offset(offset)),
+            Content(err) => Content(err),
+        }
+    }
+}
+
+impl core::fmt::Display for LaxHeaderSliceError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use LaxHeaderSliceError::*;
+        match self {
+            Len(err) => err.fmt(f),
+            Content(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for LaxHeaderSliceError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use LaxHeaderSliceError::*;
+        match self {
+            Len(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{super::HeaderError::*, LaxHeaderSliceError::*, *};
+    use crate::{err::Layer, LenSource};
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn add_slice_offset() {
+        use LaxHeaderSliceError::*;
+        assert_eq!(
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3
+            })
+            .add_slice_offset(200),
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 203
+            })
+        );
+        assert_eq!(
+            Content(UnsupportedIpVersion { version_number: 1 }).add_slice_offset(200),
+            Content(UnsupportedIpVersion { version_number: 1 })
+        );
+    }
+
+    #[test]
+    fn debug() {
+        let err = UnsupportedIpVersion { version_number: 6 };
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Content(UnsupportedIpVersion { version_number: 6 });
+        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() {
+        {
+            let err = LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Len(err)));
+        }
+        {
+            let err = UnsupportedIpVersion { version_number: 6 };
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Len(LenError {
+            required_len: 1,
+            layer: Layer::Icmpv4,
+            len: 2,
+            len_source: LenSource::Slice,
+            layer_start_offset: 3
+        })
+        .source()
+        .is_some());
+        assert!(Content(UnsupportedIpVersion { version_number: 6 })
+            .source()
+            .is_some());
+    }
+}
diff --git a/src/err/ip/mod.rs b/src/err/ip/mod.rs
new file mode 100644
index 0000000..600dfee
--- /dev/null
+++ b/src/err/ip/mod.rs
@@ -0,0 +1,24 @@
+mod header_error;
+pub use header_error::*;
+
+mod headers_error;
+pub use headers_error::*;
+
+#[cfg(feature = "std")]
+mod headers_read_error;
+#[cfg(feature = "std")]
+pub use headers_read_error::*;
+
+mod headers_slice_error;
+pub use headers_slice_error::*;
+
+#[cfg(feature = "std")]
+mod headers_write_error;
+#[cfg(feature = "std")]
+pub use headers_write_error::*;
+
+mod lax_header_slice_error;
+pub use lax_header_slice_error::*;
+
+mod slice_error;
+pub use slice_error::*;
diff --git a/src/err/ip/slice_error.rs b/src/err/ip/slice_error.rs
new file mode 100644
index 0000000..12b6b66
--- /dev/null
+++ b/src/err/ip/slice_error.rs
@@ -0,0 +1,114 @@
+use crate::err::{ip, LenError};
+
+/// Errors that can occur when slicing the IP part of a packet.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum SliceError {
+    /// Length related errors (e.g. not enough data in slice).
+    Len(LenError),
+
+    /// Error when decoding an IP header or IP extension header.
+    IpHeaders(ip::HeadersError),
+}
+
+impl core::fmt::Display for SliceError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use SliceError::*;
+        match self {
+            Len(err) => err.fmt(f),
+            IpHeaders(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for SliceError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use SliceError::*;
+        match self {
+            Len(err) => Some(err),
+            IpHeaders(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{
+        super::{HeaderError::*, HeadersError::*},
+        SliceError::*,
+    };
+    use crate::{
+        err::{Layer, LenError},
+        LenSource,
+    };
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        let err = Ip(UnsupportedIpVersion { version_number: 6 });
+        assert_eq!(
+            format!("IpHeaders({:?})", err.clone()),
+            format!("{:?}", IpHeaders(err))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = IpHeaders(Ip(UnsupportedIpVersion { version_number: 6 }));
+        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() {
+        // len
+        {
+            let err = LenError {
+                required_len: 1,
+                layer: Layer::Ipv4Packet,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Len(err)));
+        }
+        // header
+        {
+            let err = Ip(UnsupportedIpVersion { version_number: 6 });
+            assert_eq!(format!("{}", &err), format!("{}", IpHeaders(err.clone())));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Len(LenError {
+            required_len: 1,
+            layer: Layer::Ipv4Packet,
+            len: 2,
+            len_source: LenSource::Slice,
+            layer_start_offset: 3
+        })
+        .source()
+        .is_some());
+        assert!(IpHeaders(Ip(UnsupportedIpVersion { version_number: 6 }))
+            .source()
+            .is_some());
+    }
+}
diff --git a/src/err/ip_auth/header_error.rs b/src/err/ip_auth/header_error.rs
new file mode 100644
index 0000000..23a2404
--- /dev/null
+++ b/src/err/ip_auth/header_error.rs
@@ -0,0 +1,73 @@
+/// Errors that can be encountered while decoding an IP
+/// authentication header.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeaderError {
+    /// Error when the payload length is zero and therefor
+    /// too small to contain the minimum fields of the IP
+    /// authentication itself.
+    ZeroPayloadLen,
+}
+
+impl core::fmt::Display for HeaderError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderError::*;
+        match self {
+            ZeroPayloadLen => write!(f, "IP Authentication Header Error: Payload Length too small (0). The payload length must be at least 1."),
+        }
+    }
+}
+
+#[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!("ZeroPayloadLen", format!("{:?}", ZeroPayloadLen));
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = ZeroPayloadLen;
+        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!(
+            "IP Authentication Header Error: Payload Length too small (0). The payload length must be at least 1.",
+            format!("{}", ZeroPayloadLen)
+        );
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(ZeroPayloadLen.source().is_none());
+    }
+}
diff --git a/src/err/ip_auth/header_limited_read_error.rs b/src/err/ip_auth/header_limited_read_error.rs
new file mode 100644
index 0000000..efd366c
--- /dev/null
+++ b/src/err/ip_auth/header_limited_read_error.rs
@@ -0,0 +1,197 @@
+use super::HeaderError;
+use crate::err::LenError;
+
+/// Error when decoding an IP authentication header via a `std::io::Read` source.
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+#[derive(Debug)]
+pub enum HeaderLimitedReadError {
+    /// IO error was encountered while reading header.
+    Io(std::io::Error),
+
+    /// Error when parsing had to be aborted because a
+    /// length limit specified by an upper layer has been
+    /// exceeded.
+    Len(LenError),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl HeaderLimitedReadError {
+    /// Returns the `std::io::Error` value if the `HeaderLimitedReadError` is `Io`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn io(self) -> Option<std::io::Error> {
+        use HeaderLimitedReadError::*;
+        match self {
+            Io(value) => Some(value),
+            _ => None,
+        }
+    }
+
+    /// Returns the [`crate::err::LenError`] value if it is of value `Len`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn len(self) -> Option<LenError> {
+        use HeaderLimitedReadError::*;
+        match self {
+            Len(value) => Some(value),
+            _ => None,
+        }
+    }
+
+    /// Returns the [`crate::err::ip_auth::HeaderError`] value if it is of value `Content`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn content(self) -> Option<HeaderError> {
+        use HeaderLimitedReadError::*;
+        match self {
+            Content(value) => Some(value),
+            _ => None,
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl core::fmt::Display for HeaderLimitedReadError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderLimitedReadError::*;
+        match self {
+            Io(err) => write!(f, "IP Authentication Header IO Error: {}", err),
+            Len(err) => err.fmt(f),
+            Content(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderLimitedReadError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderLimitedReadError::*;
+        match self {
+            Io(err) => Some(err),
+            Len(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(all(test, feature = "std"))]
+mod test {
+    use crate::{err::Layer, LenSource};
+
+    use super::{HeaderLimitedReadError::*, *};
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::ZeroPayloadLen;
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn fmt() {
+        {
+            let err = std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            );
+            assert_eq!(
+                format!("IP Authentication Header IO Error: {}", err),
+                format!("{}", Io(err))
+            );
+        }
+        {
+            let err = LenError {
+                required_len: 2,
+                len: 1,
+                len_source: LenSource::Slice,
+                layer: Layer::IpAuthHeader,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Len(err.clone())));
+        }
+        {
+            let err = HeaderError::ZeroPayloadLen;
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[test]
+    fn source() {
+        use std::error::Error;
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .source()
+        .is_some());
+        assert!(Len(LenError {
+            required_len: 2,
+            len: 1,
+            len_source: LenSource::Slice,
+            layer: Layer::IpAuthHeader,
+            layer_start_offset: 3,
+        })
+        .source()
+        .is_some());
+        assert!(Content(HeaderError::ZeroPayloadLen).source().is_some());
+    }
+
+    #[test]
+    fn io() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .io()
+        .is_some());
+        assert!(Content(HeaderError::ZeroPayloadLen).io().is_none());
+    }
+
+    #[test]
+    fn len() {
+        assert_eq!(
+            None,
+            Io(std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            ))
+            .len()
+        );
+        {
+            let err = LenError {
+                required_len: 2,
+                len: 1,
+                len_source: LenSource::Slice,
+                layer: Layer::IpAuthHeader,
+                layer_start_offset: 3,
+            };
+            assert_eq!(Some(err.clone()), Len(err.clone()).len());
+        }
+    }
+
+    #[test]
+    fn content() {
+        assert_eq!(
+            None,
+            Io(std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            ))
+            .content()
+        );
+        {
+            let err = HeaderError::ZeroPayloadLen;
+            assert_eq!(Some(err.clone()), Content(err.clone()).content());
+        }
+    }
+}
diff --git a/src/err/ip_auth/header_read_error.rs b/src/err/ip_auth/header_read_error.rs
new file mode 100644
index 0000000..dcd5c96
--- /dev/null
+++ b/src/err/ip_auth/header_read_error.rs
@@ -0,0 +1,135 @@
+use super::HeaderError;
+
+/// Error when decoding an IP authentication header via a `std::io::Read` source.
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+#[derive(Debug)]
+pub enum HeaderReadError {
+    /// IO error was encountered while reading header.
+    Io(std::io::Error),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl HeaderReadError {
+    /// Returns the `std::io::Error` value if the `HeaderReadError` is `Io`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn io(self) -> Option<std::io::Error> {
+        use HeaderReadError::*;
+        match self {
+            Io(value) => Some(value),
+            _ => None,
+        }
+    }
+
+    /// Returns the `err::ip_auth::HeaderError` value if it is of value `Content`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn content(self) -> Option<HeaderError> {
+        use HeaderReadError::*;
+        match self {
+            Content(value) => Some(value),
+            _ => None,
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+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, "IP Authentication Header IO Error: {}", err),
+            Content(value) => value.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderReadError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderReadError::*;
+        match self {
+            Io(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(all(test, feature = "std"))]
+mod test {
+    use super::{HeaderReadError::*, *};
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::ZeroPayloadLen;
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn fmt() {
+        {
+            let err = std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            );
+            assert_eq!(
+                format!("IP Authentication Header IO Error: {}", err),
+                format!("{}", Io(err))
+            );
+        }
+        {
+            let err = HeaderError::ZeroPayloadLen;
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[test]
+    fn source() {
+        use std::error::Error;
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .source()
+        .is_some());
+        assert!(Content(HeaderError::ZeroPayloadLen).source().is_some());
+    }
+
+    #[test]
+    fn io() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .io()
+        .is_some());
+        assert!(Content(HeaderError::ZeroPayloadLen).io().is_none());
+    }
+
+    #[test]
+    fn content() {
+        assert_eq!(
+            None,
+            Io(std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            ))
+            .content()
+        );
+        {
+            let err = HeaderError::ZeroPayloadLen;
+            assert_eq!(Some(err.clone()), Content(err.clone()).content());
+        }
+    }
+}
diff --git a/src/err/ip_auth/header_slice_error.rs b/src/err/ip_auth/header_slice_error.rs
new file mode 100644
index 0000000..8512f99
--- /dev/null
+++ b/src/err/ip_auth/header_slice_error.rs
@@ -0,0 +1,144 @@
+use super::HeaderError;
+use crate::err::LenError;
+
+/// Error when decoding an IP authentication header from a slice.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeaderSliceError {
+    /// Error when an length error is encountered (e.g. unexpected
+    /// end of slice).
+    Len(LenError),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+impl HeaderSliceError {
+    /// Adds an offset value to all slice length related fields.
+    #[inline]
+    pub const fn add_slice_offset(self, offset: usize) -> Self {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => Len(err.add_offset(offset)),
+            Content(err) => Content(err),
+        }
+    }
+}
+
+impl core::fmt::Display for HeaderSliceError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => err.fmt(f),
+            Content(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderSliceError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{HeaderSliceError::*, *};
+    use crate::{err::Layer, LenSource};
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn add_slice_offset() {
+        use HeaderSliceError::*;
+        assert_eq!(
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3
+            })
+            .add_slice_offset(200),
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 203
+            })
+        );
+        assert_eq!(
+            Content(HeaderError::ZeroPayloadLen).add_slice_offset(200),
+            Content(HeaderError::ZeroPayloadLen)
+        );
+    }
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::ZeroPayloadLen;
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Content(HeaderError::ZeroPayloadLen);
+        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() {
+        {
+            let err = LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Len(err)));
+        }
+        {
+            let err = HeaderError::ZeroPayloadLen;
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Len(LenError {
+            required_len: 1,
+            layer: Layer::Icmpv4,
+            len: 2,
+            len_source: LenSource::Slice,
+            layer_start_offset: 3
+        })
+        .source()
+        .is_some());
+        assert!(Content(HeaderError::ZeroPayloadLen).source().is_some());
+    }
+}
diff --git a/src/err/ip_auth/icv_len_error.rs b/src/err/ip_auth/icv_len_error.rs
new file mode 100644
index 0000000..b92af8f
--- /dev/null
+++ b/src/err/ip_auth/icv_len_error.rs
@@ -0,0 +1,87 @@
+/// Error when creating an [`crate::IpAuthHeader`] and the
+/// length of the raw ICV is non representable in an IP authentication
+/// header.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum IcvLenError {
+    /// Error when the payload length is bigger then
+    /// [`crate::IpAuthHeader::MAX_ICV_LEN`] (1016).
+    TooBig(usize),
+
+    /// Error when the ICV length can not be represented
+    /// as a multiple of 4-bytes in the authentication header
+    /// (`0 == raw_icv.len() % 4` is not fulfilled).
+    Unaligned(usize),
+}
+
+impl core::fmt::Display for IcvLenError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use IcvLenError::*;
+        match self {
+            TooBig(size) =>
+                write!(f, "Error the IP authentication header ICV length is too large. The ICV size ({} bytes) is larger then what can be be represented by the 'payload len' field in an IP authentication header.", size),
+            Unaligned(size) =>
+                write!(f, "Error the IP authentication header ICV length of {} bytes is not a multiple of 4. This is required as the payload length field can only express lengths in multiple of 4 bytes.", size),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for IcvLenError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        None
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::IcvLenError::*;
+    use crate::*;
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        assert_eq!("TooBig(3000)", format!("{:?}", TooBig(3000)));
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = TooBig(5000);
+        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!(
+            "Error the IP authentication header ICV length is too large. The ICV size (4000 bytes) is larger then what can be be represented by the 'payload len' field in an IP authentication header.",
+            format!("{}", TooBig(4000))
+        );
+        assert_eq!(
+            "Error the IP authentication header ICV length of 12 bytes is not a multiple of 4. This is required as the payload length field can only express lengths in multiple of 4 bytes.",
+            format!("{}", Unaligned(12))
+        );
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(TooBig(4000).source().is_none());
+        assert!(Unaligned(12).source().is_none());
+    }
+}
diff --git a/src/err/ip_auth/mod.rs b/src/err/ip_auth/mod.rs
new file mode 100644
index 0000000..c070abb
--- /dev/null
+++ b/src/err/ip_auth/mod.rs
@@ -0,0 +1,18 @@
+mod header_error;
+pub use header_error::*;
+
+#[cfg(feature = "std")]
+mod header_read_error;
+#[cfg(feature = "std")]
+pub use header_read_error::*;
+
+#[cfg(feature = "std")]
+mod header_limited_read_error;
+#[cfg(feature = "std")]
+pub use header_limited_read_error::*;
+
+mod header_slice_error;
+pub use header_slice_error::*;
+
+mod icv_len_error;
+pub use icv_len_error::*;
diff --git a/src/err/ip_exts/exts_walk_error.rs b/src/err/ip_exts/exts_walk_error.rs
new file mode 100644
index 0000000..e5f2637
--- /dev/null
+++ b/src/err/ip_exts/exts_walk_error.rs
@@ -0,0 +1,101 @@
+use crate::err::{ipv4_exts, ipv6_exts};
+
+/// Errors while serializing or determining the next_header of
+/// an [`crate::IpHeaders`].
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum ExtsWalkError {
+    /// Error within the IPv4 extensions headers.
+    Ipv4Exts(ipv4_exts::ExtsWalkError),
+
+    /// Error within the IPv6 extensions headers.
+    Ipv6Exts(ipv6_exts::ExtsWalkError),
+}
+
+impl core::fmt::Display for ExtsWalkError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use ExtsWalkError::*;
+        match self {
+            Ipv4Exts(err) => err.fmt(f),
+            Ipv6Exts(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for ExtsWalkError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use ExtsWalkError::*;
+        match self {
+            Ipv4Exts(err) => Some(err),
+            Ipv6Exts(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{ExtsWalkError::*, *};
+    use crate::IpNumber;
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        let err = ipv6_exts::ExtsWalkError::HopByHopNotAtStart;
+        assert_eq!(
+            format!("Ipv6Exts({:?})", err.clone()),
+            format!("{:?}", Ipv6Exts(err))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Ipv6Exts(ipv6_exts::ExtsWalkError::HopByHopNotAtStart);
+        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() {
+        // Ipv4Exts
+        {
+            let err = ipv4_exts::ExtsWalkError::ExtNotReferenced {
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Ipv4Exts(err)));
+        }
+        // Ipv6Exts
+        {
+            let err = ipv6_exts::ExtsWalkError::HopByHopNotAtStart;
+            assert_eq!(format!("{}", &err), format!("{}", Ipv6Exts(err.clone())));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Ipv4Exts(ipv4_exts::ExtsWalkError::ExtNotReferenced {
+            missing_ext: IpNumber::AUTHENTICATION_HEADER
+        })
+        .source()
+        .is_some());
+        assert!(Ipv6Exts(ipv6_exts::ExtsWalkError::HopByHopNotAtStart)
+            .source()
+            .is_some());
+    }
+}
diff --git a/src/err/ip_exts/header_error.rs b/src/err/ip_exts/header_error.rs
new file mode 100644
index 0000000..25c5af8
--- /dev/null
+++ b/src/err/ip_exts/header_error.rs
@@ -0,0 +1,93 @@
+use crate::*;
+
+/// Error when decoding the IP extension header.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeaderError {
+    /// Error in the IPv4 extension headers (only authentication header).
+    Ipv4Ext(err::ip_auth::HeaderError),
+
+    /// Error in the IPv6 extension headers.
+    Ipv6Ext(err::ipv6_exts::HeaderError),
+}
+
+impl core::fmt::Display for HeaderError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderError::*;
+        match self {
+            Ipv4Ext(err) => err.fmt(f),
+            Ipv6Ext(err) => err.fmt(f),
+        }
+    }
+}
+
+#[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)> {
+        use HeaderError::*;
+        match self {
+            Ipv4Ext(err) => Some(err),
+            Ipv6Ext(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{super::HeaderError::*, *};
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        assert_eq!(
+            "Ipv4Ext(ZeroPayloadLen)",
+            format!("{:?}", Ipv4Ext(err::ip_auth::HeaderError::ZeroPayloadLen))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Ipv4Ext(err::ip_auth::HeaderError::ZeroPayloadLen);
+        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() {
+        {
+            let err = err::ip_auth::HeaderError::ZeroPayloadLen;
+            assert_eq!(format!("{}", Ipv4Ext(err.clone())), format!("{}", err));
+        }
+        {
+            let err = err::ipv6_exts::HeaderError::HopByHopNotAtStart;
+            assert_eq!(format!("{}", Ipv6Ext(err.clone())), format!("{}", err));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        let values = [
+            Ipv4Ext(err::ip_auth::HeaderError::ZeroPayloadLen),
+            Ipv6Ext(err::ipv6_exts::HeaderError::HopByHopNotAtStart),
+        ];
+        for v in values {
+            assert!(v.source().is_some());
+        }
+    }
+}
diff --git a/src/err/ip_exts/headers_slice_error.rs b/src/err/ip_exts/headers_slice_error.rs
new file mode 100644
index 0000000..77ea0e8
--- /dev/null
+++ b/src/err/ip_exts/headers_slice_error.rs
@@ -0,0 +1,175 @@
+use super::HeaderError;
+use crate::err::LenError;
+
+/// Error when decoding IP extension headers from a slice.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeadersSliceError {
+    /// Error when an length error is encountered (e.g. unexpected
+    /// end of slice).
+    Len(LenError),
+
+    /// Error caused by the contents of a header.
+    Content(HeaderError),
+}
+
+impl HeadersSliceError {
+    /// Returns the [`crate::err::LenError`] if the error is an Len.
+    pub fn len_error(&self) -> Option<&LenError> {
+        use HeadersSliceError::*;
+        match self {
+            Len(err) => Some(err),
+            Content(_) => None,
+        }
+    }
+
+    /// Returns the [`crate::err::ipv6_exts::HeaderError`] if the error is an Len.
+    pub fn content(&self) -> Option<&HeaderError> {
+        use HeadersSliceError::*;
+        match self {
+            Len(_) => None,
+            Content(err) => Some(err),
+        }
+    }
+}
+
+impl core::fmt::Display for HeadersSliceError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeadersSliceError::*;
+        match self {
+            Len(err) => err.fmt(f),
+            Content(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeadersSliceError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeadersSliceError::*;
+        match self {
+            Len(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{HeadersSliceError::*, *};
+    use crate::{
+        err::{ipv6_exts::HeaderError::*, Layer},
+        LenSource,
+    };
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn len_error() {
+        assert_eq!(
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3
+            })
+            .len_error(),
+            Some(&LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3
+            })
+        );
+        assert_eq!(
+            Content(HeaderError::Ipv6Ext(HopByHopNotAtStart)).len_error(),
+            None
+        );
+    }
+
+    #[test]
+    fn content() {
+        assert_eq!(
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3
+            })
+            .content(),
+            None
+        );
+        assert_eq!(
+            Content(HeaderError::Ipv6Ext(HopByHopNotAtStart)).content(),
+            Some(&HeaderError::Ipv6Ext(HopByHopNotAtStart))
+        );
+    }
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::Ipv6Ext(HopByHopNotAtStart);
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Content(HeaderError::Ipv6Ext(HopByHopNotAtStart));
+        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() {
+        {
+            let err = LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Len(err)));
+        }
+        {
+            let err = HeaderError::Ipv6Ext(HopByHopNotAtStart);
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Len(LenError {
+            required_len: 1,
+            layer: Layer::Icmpv4,
+            len: 2,
+            len_source: LenSource::Slice,
+            layer_start_offset: 3
+        })
+        .source()
+        .is_some());
+        assert!(Content(HeaderError::Ipv6Ext(HopByHopNotAtStart))
+            .source()
+            .is_some());
+    }
+}
diff --git a/src/err/ip_exts/mod.rs b/src/err/ip_exts/mod.rs
new file mode 100644
index 0000000..aa98ec0
--- /dev/null
+++ b/src/err/ip_exts/mod.rs
@@ -0,0 +1,8 @@
+mod exts_walk_error;
+pub use exts_walk_error::*;
+
+mod header_error;
+pub use header_error::*;
+
+mod headers_slice_error;
+pub use headers_slice_error::*;
diff --git a/src/err/ipv4/bad_options_len.rs b/src/err/ipv4/bad_options_len.rs
new file mode 100644
index 0000000..749c6de
--- /dev/null
+++ b/src/err/ipv4/bad_options_len.rs
@@ -0,0 +1,74 @@
+/// Error if a slice can not be used as options data in
+/// [`crate::Ipv4Options`] as then length is non compatible.
+///
+/// The length for options in an IPv4 header
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub struct BadOptionsLen {
+    /// Invalid length.
+    pub bad_len: usize,
+}
+
+impl core::fmt::Display for BadOptionsLen {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        write!(f, "Slice of length {} cannot be set as IPv4 header options. The length must be a multiple of 4 and at maximum 40.", self.bad_len)
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for BadOptionsLen {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        None
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        assert_eq!(
+            "BadOptionsLen { bad_len: 123 }",
+            format!("{:?}", BadOptionsLen { bad_len: 123 })
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = BadOptionsLen { bad_len: 123 };
+        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() {
+        let err = BadOptionsLen { bad_len: 123 };
+        assert_eq!(
+            format!("{}", err),
+            "Slice of length 123 cannot be set as IPv4 header options. The length must be a multiple of 4 and at maximum 40."
+        );
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(BadOptionsLen { bad_len: 123 }.source().is_none());
+    }
+}
diff --git a/src/err/ipv4/header_error.rs b/src/err/ipv4/header_error.rs
new file mode 100644
index 0000000..143c5e3
--- /dev/null
+++ b/src/err/ipv4/header_error.rs
@@ -0,0 +1,93 @@
+/// Error when decoding the IPv4 part of a message.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeaderError {
+    /// Error when the IPv4 header version field is not equal to 4.
+    UnexpectedVersion {
+        /// The unexpected version number in the IPv4 header.
+        version_number: u8,
+    },
+
+    /// Error when the ipv4 internet header length is smaller then the header itself (5).
+    HeaderLengthSmallerThanHeader {
+        /// The internet header length that was too small.
+        ihl: u8,
+    },
+}
+
+impl core::fmt::Display for HeaderError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderError::*;
+        match self {
+            UnexpectedVersion { version_number } => write!(f, "IPv4 Header Error: Encountered '{}' as IP version number in the IPv4 header (must be '4' in an IPv4 header).", version_number),
+            HeaderLengthSmallerThanHeader { ihl } => write!(f, "IPv4 Header Error: The 'internet header length' value '{}' present in the IPv4 header is smaller than the minimum size of an IPv4 header. The minimum allowed value is '5'.", ihl),
+        }
+    }
+}
+
+#[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 { version_number: 6 }",
+            format!("{:?}", UnexpectedVersion { version_number: 6 })
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = HeaderError::UnexpectedVersion { version_number: 6 };
+        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!(
+            "IPv4 Header Error: Encountered '1' as IP version number in the IPv4 header (must be '4' in an IPv4 header).",
+            format!("{}", UnexpectedVersion{ version_number: 1 })
+        );
+        assert_eq!(
+            "IPv4 Header Error: The 'internet header length' value '2' present in the IPv4 header is smaller than the minimum size of an IPv4 header. The minimum allowed value is '5'.",
+            format!("{}", HeaderLengthSmallerThanHeader{ ihl: 2 })
+        );
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        let values = [
+            UnexpectedVersion { version_number: 0 },
+            HeaderLengthSmallerThanHeader { ihl: 0 },
+        ];
+        for v in values {
+            assert!(v.source().is_none());
+        }
+    }
+}
diff --git a/src/err/ipv4/header_read_error.rs b/src/err/ipv4/header_read_error.rs
new file mode 100644
index 0000000..6435875
--- /dev/null
+++ b/src/err/ipv4/header_read_error.rs
@@ -0,0 +1,143 @@
+use super::HeaderError;
+
+/// Error when decoding an IPv4 header via a `std::io::Read` source.
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+#[derive(Debug)]
+pub enum HeaderReadError {
+    /// IO error was encountered while reading header.
+    Io(std::io::Error),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl HeaderReadError {
+    /// Returns the `std::io::Error` value if the `HeaderReadError` is `Io`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn io_error(self) -> Option<std::io::Error> {
+        use HeaderReadError::*;
+        match self {
+            Io(value) => Some(value),
+            _ => None,
+        }
+    }
+
+    /// Returns the `err::ipv4::HeaderError` value if the `HeaderReadError` is `Content`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn content_error(self) -> Option<HeaderError> {
+        use HeaderReadError::*;
+        match self {
+            Content(value) => Some(value),
+            _ => None,
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+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, "IPv4 Header IO Error: {}", err),
+            Content(value) => value.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderReadError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderReadError::*;
+        match self {
+            Io(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(all(test, feature = "std"))]
+mod test {
+    use super::{HeaderReadError::*, *};
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::UnexpectedVersion { version_number: 6 };
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn fmt() {
+        {
+            let err = std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            );
+            assert_eq!(
+                format!("IPv4 Header IO Error: {}", err),
+                format!("{}", Io(err))
+            );
+        }
+        {
+            let err = HeaderError::UnexpectedVersion { version_number: 6 };
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[test]
+    fn source() {
+        use std::error::Error;
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .source()
+        .is_some());
+        assert!(
+            Content(HeaderError::UnexpectedVersion { version_number: 6 })
+                .source()
+                .is_some()
+        );
+    }
+
+    #[test]
+    fn io_error() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .io_error()
+        .is_some());
+        assert!(
+            Content(HeaderError::UnexpectedVersion { version_number: 6 })
+                .io_error()
+                .is_none()
+        );
+    }
+
+    #[test]
+    fn content_error() {
+        assert_eq!(
+            None,
+            Io(std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            ))
+            .content_error()
+        );
+        {
+            let err = HeaderError::UnexpectedVersion { version_number: 6 };
+            assert_eq!(Some(err.clone()), Content(err.clone()).content_error());
+        }
+    }
+}
diff --git a/src/err/ipv4/header_slice_error.rs b/src/err/ipv4/header_slice_error.rs
new file mode 100644
index 0000000..27d64fb
--- /dev/null
+++ b/src/err/ipv4/header_slice_error.rs
@@ -0,0 +1,148 @@
+use super::HeaderError;
+use crate::err::LenError;
+
+/// Error when decoding an IPv4 header from a slice.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeaderSliceError {
+    /// Error when an length error is encountered (e.g. unexpected
+    /// end of slice).
+    Len(LenError),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+impl HeaderSliceError {
+    /// Adds an offset value to all slice length related fields.
+    #[inline]
+    pub const fn add_slice_offset(self, offset: usize) -> Self {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => Len(err.add_offset(offset)),
+            Content(err) => Content(err),
+        }
+    }
+}
+
+impl core::fmt::Display for HeaderSliceError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => err.fmt(f),
+            Content(value) => value.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderSliceError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{HeaderSliceError::*, *};
+    use crate::{err::Layer, LenSource};
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn add_slice_offset() {
+        use HeaderSliceError::*;
+        assert_eq!(
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3
+            })
+            .add_slice_offset(200),
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 203
+            })
+        );
+        assert_eq!(
+            Content(HeaderError::UnexpectedVersion { version_number: 1 }).add_slice_offset(200),
+            Content(HeaderError::UnexpectedVersion { version_number: 1 })
+        );
+    }
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::UnexpectedVersion { version_number: 6 };
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Content(HeaderError::UnexpectedVersion { version_number: 6 });
+        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() {
+        {
+            let err = LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Len(err)));
+        }
+        {
+            let err = HeaderError::UnexpectedVersion { version_number: 6 };
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Len(LenError {
+            required_len: 1,
+            layer: Layer::Icmpv4,
+            len: 2,
+            len_source: LenSource::Slice,
+            layer_start_offset: 3
+        })
+        .source()
+        .is_some());
+        assert!(
+            Content(HeaderError::UnexpectedVersion { version_number: 6 })
+                .source()
+                .is_some()
+        );
+    }
+}
diff --git a/src/err/ipv4/mod.rs b/src/err/ipv4/mod.rs
new file mode 100644
index 0000000..7863dc7
--- /dev/null
+++ b/src/err/ipv4/mod.rs
@@ -0,0 +1,16 @@
+mod bad_options_len;
+pub use bad_options_len::*;
+
+mod header_error;
+pub use header_error::*;
+
+#[cfg(feature = "std")]
+mod header_read_error;
+#[cfg(feature = "std")]
+pub use header_read_error::*;
+
+mod header_slice_error;
+pub use header_slice_error::*;
+
+mod slice_error;
+pub use slice_error::*;
diff --git a/src/err/ipv4/slice_error.rs b/src/err/ipv4/slice_error.rs
new file mode 100644
index 0000000..3e5948a
--- /dev/null
+++ b/src/err/ipv4/slice_error.rs
@@ -0,0 +1,121 @@
+use crate::err::{ip_auth, ipv4, LenError};
+
+/// Errors that can occur when slicing the IPv4 part of a packet.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum SliceError {
+    /// Length related errors (e.g. not enough data in slice).
+    Len(LenError),
+
+    /// Error while slicing the header.
+    Header(ipv4::HeaderError),
+
+    /// Error while slicing an ipv4 extension header.
+    Exts(ip_auth::HeaderError),
+}
+
+impl core::fmt::Display for SliceError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use SliceError::*;
+        match self {
+            Len(value) => value.fmt(f),
+            Header(err) => err.fmt(f),
+            Exts(value) => value.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for SliceError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use SliceError::*;
+        match self {
+            Len(err) => Some(err),
+            Header(err) => Some(err),
+            Exts(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{super::HeaderError, SliceError::*, *};
+    use crate::{err::Layer, LenSource};
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::UnexpectedVersion { version_number: 6 };
+        assert_eq!(
+            format!("Header({:?})", err.clone()),
+            format!("{:?}", Header(err))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Header(HeaderError::UnexpectedVersion { version_number: 6 });
+        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() {
+        // len
+        {
+            let err = LenError {
+                required_len: 1,
+                layer: Layer::Ipv4Packet,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Len(err)));
+        }
+        // header
+        {
+            let err = HeaderError::UnexpectedVersion { version_number: 6 };
+            assert_eq!(format!("{}", &err), format!("{}", Header(err.clone())));
+        }
+        // extensions
+        {
+            let err = ip_auth::HeaderError::ZeroPayloadLen;
+            assert_eq!(format!("{}", &err), format!("{}", Exts(err.clone())));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Len(LenError {
+            required_len: 1,
+            layer: Layer::Ipv4Packet,
+            len: 2,
+            len_source: LenSource::Slice,
+            layer_start_offset: 3
+        })
+        .source()
+        .is_some());
+        assert!(Header(HeaderError::UnexpectedVersion { version_number: 6 })
+            .source()
+            .is_some());
+        assert!(Exts(ip_auth::HeaderError::ZeroPayloadLen)
+            .source()
+            .is_some());
+    }
+}
diff --git a/src/err/ipv4_exts/exts_walk_error.rs b/src/err/ipv4_exts/exts_walk_error.rs
new file mode 100644
index 0000000..4064e96
--- /dev/null
+++ b/src/err/ipv4_exts/exts_walk_error.rs
@@ -0,0 +1,107 @@
+use crate::IpNumber;
+
+/// Errors in content of IPv4 header extensions that prevent serialization
+/// or determining the next header.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum ExtsWalkError {
+    /// Error when a header in [`crate::Ipv4Extensions`] is never referenced even
+    /// though it is present in the [`crate::Ipv4Extensions`].
+    ///
+    /// This can occur when calculating the "next header" value or when
+    /// trying to write [crate::Ipv4Extensions`].
+    ExtNotReferenced {
+        /// IpNumber of the header which was not referenced.
+        missing_ext: IpNumber,
+    },
+}
+
+impl core::fmt::Display for ExtsWalkError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use ExtsWalkError::*;
+        match self {
+            ExtNotReferenced{ missing_ext } => write!(
+                f,
+                "IPv4 extensions '{:?}' is defined but is not referenced by the 'protocol' the IPv4 header.",
+                missing_ext
+            ),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for ExtsWalkError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use ExtsWalkError::*;
+        match self {
+            ExtNotReferenced { missing_ext: _ } => None,
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::ExtsWalkError::*;
+    use crate::*;
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        assert_eq!(
+            format!(
+                "ExtNotReferenced {{ missing_ext: {:?} }}",
+                IpNumber::AUTHENTICATION_HEADER,
+            ),
+            format!(
+                "{:?}",
+                ExtNotReferenced {
+                    missing_ext: IpNumber::AUTHENTICATION_HEADER
+                }
+            )
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = ExtNotReferenced {
+            missing_ext: IpNumber::AUTHENTICATION_HEADER,
+        };
+        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!(
+            "IPv4 extensions '51 (AH - Authentication Header)' is defined but is not referenced by the 'protocol' the IPv4 header.",
+            format!("{}", ExtNotReferenced{
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            })
+        );
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(ExtNotReferenced {
+            missing_ext: IpNumber::IPV6_FRAGMENTATION_HEADER
+        }
+        .source()
+        .is_none());
+    }
+}
diff --git a/src/err/ipv4_exts/header_write_error.rs b/src/err/ipv4_exts/header_write_error.rs
new file mode 100644
index 0000000..1f962bc
--- /dev/null
+++ b/src/err/ipv4_exts/header_write_error.rs
@@ -0,0 +1,140 @@
+#[cfg(feature = "std")]
+use super::ExtsWalkError;
+
+/// Error when writing IPv4 extension headers.
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+#[derive(Debug)]
+pub enum HeaderWriteError {
+    /// IO error encountered while writing.
+    Io(std::io::Error),
+    /// Data was not serializable because of its content.
+    Content(ExtsWalkError),
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl HeaderWriteError {
+    /// Returns a reference to the [`std::io::Error`] if the value is an [`HeaderWriteError::Io`].
+    pub fn io(&self) -> Option<&std::io::Error> {
+        match self {
+            HeaderWriteError::Io(err) => Some(err),
+            HeaderWriteError::Content(_) => None,
+        }
+    }
+
+    /// Returns a reference to the [`crate::err::ipv4_exts::ExtsWalkError`] if the value is an [`HeaderWriteError::Content`].
+    pub fn content(&self) -> Option<&ExtsWalkError> {
+        match self {
+            HeaderWriteError::Io(_) => None,
+            HeaderWriteError::Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl core::fmt::Display for HeaderWriteError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderWriteError::*;
+        match self {
+            Io(err) => err.fmt(f),
+            Content(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderWriteError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderWriteError::*;
+        match self {
+            Io(ref err) => Some(err),
+            Content(ref err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{ExtsWalkError::*, HeaderWriteError::*};
+    use crate::*;
+    use alloc::format;
+    use std::error::Error;
+
+    #[test]
+    fn io() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .io()
+        .is_some());
+        assert!(Content(ExtNotReferenced {
+            missing_ext: IpNumber::AUTHENTICATION_HEADER,
+        })
+        .io()
+        .is_none());
+    }
+
+    #[test]
+    fn content() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .content()
+        .is_none());
+        {
+            let err = ExtNotReferenced {
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            };
+            assert_eq!(Some(&err), Content(err.clone()).content());
+        }
+    }
+
+    #[test]
+    fn debug() {
+        let err = ExtNotReferenced {
+            missing_ext: IpNumber::AUTHENTICATION_HEADER,
+        };
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn fmt() {
+        {
+            let err = std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            );
+            assert_eq!(format!("{}", err), format!("{}", Io(err)));
+        }
+        {
+            let err = ExtNotReferenced {
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            };
+            assert_eq!(format!("{}", Content(err.clone())), format!("{}", err));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .source()
+        .is_some());
+        assert!(Content(ExtNotReferenced {
+            missing_ext: IpNumber::AUTHENTICATION_HEADER,
+        })
+        .source()
+        .is_some());
+    }
+}
diff --git a/src/err/ipv4_exts/mod.rs b/src/err/ipv4_exts/mod.rs
new file mode 100644
index 0000000..83a58aa
--- /dev/null
+++ b/src/err/ipv4_exts/mod.rs
@@ -0,0 +1,7 @@
+mod exts_walk_error;
+pub use exts_walk_error::*;
+
+#[cfg(feature = "std")]
+mod header_write_error;
+#[cfg(feature = "std")]
+pub use header_write_error::*;
diff --git a/src/err/ipv6/header_error.rs b/src/err/ipv6/header_error.rs
new file mode 100644
index 0000000..dd10808
--- /dev/null
+++ b/src/err/ipv6/header_error.rs
@@ -0,0 +1,79 @@
+/// Error when decoding the IPv6 header.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeaderError {
+    /// Error when the IPv6 header version field is not equal to 6.
+    UnexpectedVersion {
+        /// The unexpected version number in the IPv6 header.
+        version_number: u8,
+    },
+}
+
+impl core::fmt::Display for HeaderError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderError::*;
+        match self {
+            UnexpectedVersion { version_number } => write!(f, "IPv6 Header Error: Encountered '{}' as IP version number in the IPv6 header (must be '6' in an IPv6 header).", version_number),
+        }
+    }
+}
+
+#[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 { version_number: 1 }",
+            format!("{:?}", UnexpectedVersion { version_number: 1 })
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = HeaderError::UnexpectedVersion { version_number: 6 };
+        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!(
+            "IPv6 Header Error: Encountered '1' as IP version number in the IPv6 header (must be '6' in an IPv6 header).",
+            format!("{}", UnexpectedVersion{ version_number: 1 })
+        );
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        let values = [UnexpectedVersion { version_number: 0 }];
+        for v in values {
+            assert!(v.source().is_none());
+        }
+    }
+}
diff --git a/src/err/ipv6/header_read_error.rs b/src/err/ipv6/header_read_error.rs
new file mode 100644
index 0000000..9c6afae
--- /dev/null
+++ b/src/err/ipv6/header_read_error.rs
@@ -0,0 +1,143 @@
+use super::HeaderError;
+
+/// Error when decoding an IPv6 header via a `std::io::Read` source.
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+#[derive(Debug)]
+pub enum HeaderReadError {
+    /// IO error was encountered while reading header.
+    Io(std::io::Error),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl HeaderReadError {
+    /// Returns the `std::io::Error` value if the `HeaderReadError` is `Io`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn io_error(self) -> Option<std::io::Error> {
+        use HeaderReadError::*;
+        match self {
+            Io(value) => Some(value),
+            _ => None,
+        }
+    }
+
+    /// Returns the `err::ipv6::HeaderError` value if the `HeaderReadError` is `Content`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn content_error(self) -> Option<HeaderError> {
+        use HeaderReadError::*;
+        match self {
+            Content(value) => Some(value),
+            _ => None,
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+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, "IPv6 Header IO Error: {}", err),
+            Content(value) => value.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderReadError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderReadError::*;
+        match self {
+            Io(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(all(test, feature = "std"))]
+mod test {
+    use super::{HeaderReadError::*, *};
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::UnexpectedVersion { version_number: 6 };
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn fmt() {
+        {
+            let err = std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            );
+            assert_eq!(
+                format!("IPv6 Header IO Error: {}", err),
+                format!("{}", Io(err))
+            );
+        }
+        {
+            let err = HeaderError::UnexpectedVersion { version_number: 6 };
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[test]
+    fn source() {
+        use std::error::Error;
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .source()
+        .is_some());
+        assert!(
+            Content(HeaderError::UnexpectedVersion { version_number: 6 })
+                .source()
+                .is_some()
+        );
+    }
+
+    #[test]
+    fn io_error() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .io_error()
+        .is_some());
+        assert!(
+            Content(HeaderError::UnexpectedVersion { version_number: 6 })
+                .io_error()
+                .is_none()
+        );
+    }
+
+    #[test]
+    fn content_error() {
+        assert_eq!(
+            None,
+            Io(std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            ))
+            .content_error()
+        );
+        {
+            let err = HeaderError::UnexpectedVersion { version_number: 6 };
+            assert_eq!(Some(err.clone()), Content(err.clone()).content_error());
+        }
+    }
+}
diff --git a/src/err/ipv6/header_slice_error.rs b/src/err/ipv6/header_slice_error.rs
new file mode 100644
index 0000000..d491833
--- /dev/null
+++ b/src/err/ipv6/header_slice_error.rs
@@ -0,0 +1,148 @@
+use super::HeaderError;
+use crate::err::LenError;
+
+/// Error when decoding an IPv6 header from a slice.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeaderSliceError {
+    /// Error when an length error is encountered (e.g. unexpected
+    /// end of slice).
+    Len(LenError),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+impl HeaderSliceError {
+    /// Adds an offset value to all slice length related fields.
+    #[inline]
+    pub const fn add_slice_offset(self, offset: usize) -> Self {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => Len(err.add_offset(offset)),
+            Content(err) => Content(err),
+        }
+    }
+}
+
+impl core::fmt::Display for HeaderSliceError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => err.fmt(f),
+            Content(value) => value.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderSliceError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{HeaderSliceError::*, *};
+    use crate::{err::Layer, LenSource};
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn add_slice_offset() {
+        use HeaderSliceError::*;
+        assert_eq!(
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3
+            })
+            .add_slice_offset(200),
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 203
+            })
+        );
+        assert_eq!(
+            Content(HeaderError::UnexpectedVersion { version_number: 1 }).add_slice_offset(200),
+            Content(HeaderError::UnexpectedVersion { version_number: 1 })
+        );
+    }
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::UnexpectedVersion { version_number: 6 };
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Content(HeaderError::UnexpectedVersion { version_number: 6 });
+        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() {
+        {
+            let err = LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Len(err)));
+        }
+        {
+            let err = HeaderError::UnexpectedVersion { version_number: 6 };
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Len(LenError {
+            required_len: 1,
+            layer: Layer::Icmpv4,
+            len: 2,
+            len_source: LenSource::Slice,
+            layer_start_offset: 3
+        })
+        .source()
+        .is_some());
+        assert!(
+            Content(HeaderError::UnexpectedVersion { version_number: 6 })
+                .source()
+                .is_some()
+        );
+    }
+}
diff --git a/src/err/ipv6/mod.rs b/src/err/ipv6/mod.rs
new file mode 100644
index 0000000..4178c15
--- /dev/null
+++ b/src/err/ipv6/mod.rs
@@ -0,0 +1,13 @@
+mod header_error;
+pub use header_error::*;
+
+#[cfg(feature = "std")]
+mod header_read_error;
+#[cfg(feature = "std")]
+pub use header_read_error::*;
+
+mod header_slice_error;
+pub use header_slice_error::*;
+
+mod slice_error;
+pub use slice_error::*;
diff --git a/src/err/ipv6/slice_error.rs b/src/err/ipv6/slice_error.rs
new file mode 100644
index 0000000..091a060
--- /dev/null
+++ b/src/err/ipv6/slice_error.rs
@@ -0,0 +1,126 @@
+use crate::err::{ipv6, ipv6_exts, LenError};
+
+/// Errors that can occur when slicing the IPv6 part of a packet.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum SliceError {
+    /// Length related errors (e.g. not enough data in slice).
+    Len(LenError),
+
+    /// Error while slicing the header.
+    Header(ipv6::HeaderError),
+
+    /// Error while slicing an ipv6 extension header.
+    Exts(ipv6_exts::HeaderError),
+}
+
+impl core::fmt::Display for SliceError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use SliceError::*;
+        match self {
+            Len(value) => value.fmt(f),
+            Header(err) => err.fmt(f),
+            Exts(value) => value.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for SliceError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use SliceError::*;
+        match self {
+            Len(err) => Some(err),
+            Header(err) => Some(err),
+            Exts(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{super::HeaderError, SliceError::*, *};
+    use crate::{
+        err::{ip_auth, Layer},
+        LenSource,
+    };
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::UnexpectedVersion { version_number: 6 };
+        assert_eq!(
+            format!("Header({:?})", err.clone()),
+            format!("{:?}", Header(err))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Header(HeaderError::UnexpectedVersion { version_number: 6 });
+        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() {
+        // len
+        {
+            let err = LenError {
+                required_len: 1,
+                layer: Layer::Ipv6Packet,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Len(err)));
+        }
+        // header
+        {
+            let err = HeaderError::UnexpectedVersion { version_number: 6 };
+            assert_eq!(format!("{}", &err), format!("{}", Header(err.clone())));
+        }
+        // extensions
+        {
+            let err = ipv6_exts::HeaderError::IpAuth(ip_auth::HeaderError::ZeroPayloadLen);
+            assert_eq!(format!("{}", &err), format!("{}", Exts(err.clone())));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Len(LenError {
+            required_len: 1,
+            layer: Layer::Ipv4Packet,
+            len: 2,
+            len_source: LenSource::Slice,
+            layer_start_offset: 3
+        })
+        .source()
+        .is_some());
+        assert!(Header(HeaderError::UnexpectedVersion { version_number: 6 })
+            .source()
+            .is_some());
+        assert!(Exts(ipv6_exts::HeaderError::IpAuth(
+            ip_auth::HeaderError::ZeroPayloadLen
+        ))
+        .source()
+        .is_some());
+    }
+}
diff --git a/src/err/ipv6_exts/ext_payload_len_error.rs b/src/err/ipv6_exts/ext_payload_len_error.rs
new file mode 100644
index 0000000..b68a618
--- /dev/null
+++ b/src/err/ipv6_exts/ext_payload_len_error.rs
@@ -0,0 +1,97 @@
+/// Error when creating an [`crate::Ipv6RawExtHeader`] and the
+/// payload len is non representable.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum ExtPayloadLenError {
+    /// Error when the payload length is smaller then
+    /// [`crate::Ipv6RawExtHeader::MIN_PAYLOAD_LEN`] (6).
+    TooSmall(usize),
+
+    /// Error when the payload length is bigger then
+    /// [`crate::Ipv6RawExtHeader::MAX_PAYLOAD_LEN`] (2046).
+    TooBig(usize),
+
+    /// Error when the payload length can not be represented
+    /// as a multiple of 8-bytes in the extension header
+    /// (`0 == (payload.len() + 2) % 8` is not fulfilled).
+    Unaligned(usize),
+}
+
+impl core::fmt::Display for ExtPayloadLenError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use ExtPayloadLenError::*;
+        match self {
+            TooSmall(size) =>
+                write!(f, "IPv6 extensions header payload length is too small. The payload size ({} bytes) is less then 6 octets which is the minimum IPv6 extension header payload size.", size),
+            TooBig(size) =>
+                write!(f, "IPv6 extensions header payload length is too large. The payload size ({} bytes) is larger then what can be be represented by the 'extended header size' field in an IPv6 extension header.", size),
+            Unaligned(size) =>
+                write!(f, "IPv6 extensions header 'payload length ({} bytes) + 2' is not multiple of 8 (+ 2 for the `next_header` and `header_length` fields). This is required as the header length field can only express lengths in multiple of 8 bytes.", size),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for ExtPayloadLenError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        None
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::ExtPayloadLenError::*;
+    use crate::*;
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        assert_eq!("TooBig(3000)", format!("{:?}", TooBig(3000)));
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = TooBig(5000);
+        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!(
+            "IPv6 extensions header payload length is too small. The payload size (2 bytes) is less then 6 octets which is the minimum IPv6 extension header payload size.",
+            format!("{}", TooSmall(2))
+        );
+        assert_eq!(
+            "IPv6 extensions header payload length is too large. The payload size (4000 bytes) is larger then what can be be represented by the 'extended header size' field in an IPv6 extension header.",
+            format!("{}", TooBig(4000))
+        );
+        assert_eq!(
+            "IPv6 extensions header 'payload length (12 bytes) + 2' is not multiple of 8 (+ 2 for the `next_header` and `header_length` fields). This is required as the header length field can only express lengths in multiple of 8 bytes.",
+            format!("{}", Unaligned(12))
+        );
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(TooSmall(1).source().is_none());
+        assert!(TooBig(4000).source().is_none());
+        assert!(Unaligned(12).source().is_none());
+    }
+}
diff --git a/src/err/ipv6_exts/exts_walk_error.rs b/src/err/ipv6_exts/exts_walk_error.rs
new file mode 100644
index 0000000..0365380
--- /dev/null
+++ b/src/err/ipv6_exts/exts_walk_error.rs
@@ -0,0 +1,94 @@
+use crate::IpNumber;
+
+/// Errors in content of IPv6 header extensions that prevent serialization.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum ExtsWalkError {
+    /// Error when a hop-by-hop header is not referenced as the
+    /// first header after the ipv6 header but as a later extension
+    /// header.
+    HopByHopNotAtStart,
+
+    /// Error when a header in [`crate::Ipv6Extensions`] is never written
+    /// as it is never referenced by any of the other `next_header`
+    /// fields or the initial ip number.
+    ExtNotReferenced {
+        /// IpNumber of the header which was not referenced.
+        missing_ext: IpNumber,
+    },
+}
+
+impl core::fmt::Display for ExtsWalkError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        match self {
+            ExtsWalkError::HopByHopNotAtStart =>
+                write!(f, "IPv6 extensions hop-by-hop is not located directly after the IPv6 header (required by IPv6)."),
+            ExtsWalkError::ExtNotReferenced{ missing_ext } =>
+                write!(f, "IPv6 extensions '{:?}' is defined but is not referenced by any of the 'next_header' of the other extension headers or the IPv6 header.", missing_ext),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for ExtsWalkError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        None
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::ExtsWalkError::*;
+    use crate::*;
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        assert_eq!("HopByHopNotAtStart", format!("{:?}", HopByHopNotAtStart));
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = HopByHopNotAtStart;
+        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!(
+            "IPv6 extensions hop-by-hop is not located directly after the IPv6 header (required by IPv6).",
+            format!("{}", HopByHopNotAtStart)
+        );
+        assert_eq!(
+            "IPv6 extensions '44 (IPv6-Frag - Fragment Header for IPv6)' is defined but is not referenced by any of the 'next_header' of the other extension headers or the IPv6 header.",
+            format!("{}", ExtNotReferenced{ missing_ext: IpNumber::IPV6_FRAGMENTATION_HEADER })
+        );
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(HopByHopNotAtStart.source().is_none());
+        assert!(ExtNotReferenced {
+            missing_ext: IpNumber::IPV6_FRAGMENTATION_HEADER
+        }
+        .source()
+        .is_none());
+    }
+}
diff --git a/src/err/ipv6_exts/header_error.rs b/src/err/ipv6_exts/header_error.rs
new file mode 100644
index 0000000..28c45e0
--- /dev/null
+++ b/src/err/ipv6_exts/header_error.rs
@@ -0,0 +1,88 @@
+use crate::*;
+
+/// Error when decoding IPv6 extension headers.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeaderError {
+    /// Error if the ipv6 hop by hop header does not occur directly after the ipv6 header (see rfc8200 chapter 4.1.)
+    HopByHopNotAtStart,
+
+    /// Error in the ip authentication header.
+    IpAuth(err::ip_auth::HeaderError),
+}
+
+impl core::fmt::Display for HeaderError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderError::*;
+        match self {
+            HopByHopNotAtStart => write!(f, "IPv6 Extension Header Error: Encountered an IPv6 hop-by-hop header not directly after the IPv6 header. This is not allowed according to RFC 8200."),
+            IpAuth(err) => err.fmt(f),
+        }
+    }
+}
+
+#[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)> {
+        use HeaderError::*;
+        match self {
+            HopByHopNotAtStart => None,
+            IpAuth(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::HeaderError::*;
+    use crate::*;
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        assert_eq!("HopByHopNotAtStart", format!("{:?}", HopByHopNotAtStart));
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = HopByHopNotAtStart;
+        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!(
+            "IPv6 Extension Header Error: Encountered an IPv6 hop-by-hop header not directly after the IPv6 header. This is not allowed according to RFC 8200.",
+            format!("{}", HopByHopNotAtStart)
+        );
+        {
+            let err = err::ip_auth::HeaderError::ZeroPayloadLen;
+            assert_eq!(format!("{}", err), format!("{}", IpAuth(err)));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        use err::ip_auth::HeaderError::ZeroPayloadLen;
+
+        assert!(HopByHopNotAtStart.source().is_none());
+        assert!(IpAuth(ZeroPayloadLen).source().is_some());
+    }
+}
diff --git a/src/err/ipv6_exts/header_limited_read_error.rs b/src/err/ipv6_exts/header_limited_read_error.rs
new file mode 100644
index 0000000..0e32035
--- /dev/null
+++ b/src/err/ipv6_exts/header_limited_read_error.rs
@@ -0,0 +1,197 @@
+use super::HeaderError;
+use crate::err::LenError;
+
+/// Error when decoding IPv6 extension headers via a `std::io::Read` source.
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+#[derive(Debug)]
+pub enum HeaderLimitedReadError {
+    /// IO error was encountered while reading header.
+    Io(std::io::Error),
+
+    /// Error when parsing had to be aborted because a
+    /// length limit specified by an upper layer has been
+    /// exceeded.
+    Len(LenError),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl HeaderLimitedReadError {
+    /// Returns the [`std::io::Error`] value if the [`HeaderLimitedReadError`] is `Io`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn io(self) -> Option<std::io::Error> {
+        use HeaderLimitedReadError::*;
+        match self {
+            Io(value) => Some(value),
+            _ => None,
+        }
+    }
+
+    /// Returns the [`crate::err::LenError`] value if it is of value `Len`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn len(self) -> Option<LenError> {
+        use HeaderLimitedReadError::*;
+        match self {
+            Len(value) => Some(value),
+            _ => None,
+        }
+    }
+
+    /// Returns the [`crate::err::ip_auth::HeaderError`] value if it is of value `Content`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn content(self) -> Option<HeaderError> {
+        use HeaderLimitedReadError::*;
+        match self {
+            Content(value) => Some(value),
+            _ => None,
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl core::fmt::Display for HeaderLimitedReadError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderLimitedReadError::*;
+        match self {
+            Io(err) => write!(f, "IPv6 Extension Header IO Error: {}", err),
+            Len(err) => err.fmt(f),
+            Content(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderLimitedReadError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderLimitedReadError::*;
+        match self {
+            Io(err) => Some(err),
+            Len(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(all(test, feature = "std"))]
+mod test {
+    use crate::{err::Layer, LenSource};
+
+    use super::{HeaderLimitedReadError::*, *};
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::HopByHopNotAtStart;
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn fmt() {
+        {
+            let err = std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            );
+            assert_eq!(
+                format!("IPv6 Extension Header IO Error: {}", err),
+                format!("{}", Io(err))
+            );
+        }
+        {
+            let err = LenError {
+                required_len: 2,
+                len: 1,
+                len_source: LenSource::Slice,
+                layer: Layer::IpAuthHeader,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Len(err.clone())));
+        }
+        {
+            let err = HeaderError::HopByHopNotAtStart;
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[test]
+    fn source() {
+        use std::error::Error;
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .source()
+        .is_some());
+        assert!(Len(LenError {
+            required_len: 2,
+            len: 1,
+            len_source: LenSource::Slice,
+            layer: Layer::IpAuthHeader,
+            layer_start_offset: 3,
+        })
+        .source()
+        .is_some());
+        assert!(Content(HeaderError::HopByHopNotAtStart).source().is_some());
+    }
+
+    #[test]
+    fn io() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .io()
+        .is_some());
+        assert!(Content(HeaderError::HopByHopNotAtStart).io().is_none());
+    }
+
+    #[test]
+    fn len() {
+        assert_eq!(
+            None,
+            Io(std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            ))
+            .len()
+        );
+        {
+            let err = LenError {
+                required_len: 2,
+                len: 1,
+                len_source: LenSource::Slice,
+                layer: Layer::IpAuthHeader,
+                layer_start_offset: 3,
+            };
+            assert_eq!(Some(err.clone()), Len(err.clone()).len());
+        }
+    }
+
+    #[test]
+    fn content() {
+        assert_eq!(
+            None,
+            Io(std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            ))
+            .content()
+        );
+        {
+            let err = HeaderError::HopByHopNotAtStart;
+            assert_eq!(Some(err.clone()), Content(err.clone()).content());
+        }
+    }
+}
diff --git a/src/err/ipv6_exts/header_read_error.rs b/src/err/ipv6_exts/header_read_error.rs
new file mode 100644
index 0000000..a6e8779
--- /dev/null
+++ b/src/err/ipv6_exts/header_read_error.rs
@@ -0,0 +1,137 @@
+use super::HeaderError;
+
+/// Error when decoding IPv6 extension headers via a `std::io::Read` source.
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+#[derive(Debug)]
+pub enum HeaderReadError {
+    /// IO error was encountered while reading header.
+    Io(std::io::Error),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl HeaderReadError {
+    /// Returns the `std::io::Error` value if the `HeaderReadError` is `Io`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn io_error(self) -> Option<std::io::Error> {
+        use HeaderReadError::*;
+        match self {
+            Io(value) => Some(value),
+            _ => None,
+        }
+    }
+
+    /// Returns the `err::ipv6_exts::HeaderError` value if the `HeaderReadError` is `Content`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn content_error(self) -> Option<HeaderError> {
+        use HeaderReadError::*;
+        match self {
+            Content(value) => Some(value),
+            _ => None,
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+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, "IPv6 Extension Header IO Error: {}", err),
+            Content(value) => value.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderReadError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderReadError::*;
+        match self {
+            Io(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(all(test, feature = "std"))]
+mod test {
+    use super::{HeaderReadError::*, *};
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::HopByHopNotAtStart;
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn fmt() {
+        {
+            let err = std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            );
+            assert_eq!(
+                format!("IPv6 Extension Header IO Error: {}", err),
+                format!("{}", Io(err))
+            );
+        }
+        {
+            let err = HeaderError::HopByHopNotAtStart;
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[test]
+    fn source() {
+        use std::error::Error;
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .source()
+        .is_some());
+        assert!(Content(HeaderError::HopByHopNotAtStart).source().is_some());
+    }
+
+    #[test]
+    fn io_error() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .io_error()
+        .is_some());
+        assert!(Content(HeaderError::HopByHopNotAtStart)
+            .io_error()
+            .is_none());
+    }
+
+    #[test]
+    fn content_error() {
+        assert_eq!(
+            None,
+            Io(std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            ))
+            .content_error()
+        );
+        {
+            let err = HeaderError::HopByHopNotAtStart;
+            assert_eq!(Some(err.clone()), Content(err.clone()).content_error());
+        }
+    }
+}
diff --git a/src/err/ipv6_exts/header_slice_error.rs b/src/err/ipv6_exts/header_slice_error.rs
new file mode 100644
index 0000000..6473369
--- /dev/null
+++ b/src/err/ipv6_exts/header_slice_error.rs
@@ -0,0 +1,202 @@
+use super::HeaderError;
+use crate::err::LenError;
+
+/// Error when decoding IPv6 extension headers from a slice.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeaderSliceError {
+    /// Error when an length error is encountered (e.g. unexpected
+    /// end of slice).
+    Len(LenError),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+impl HeaderSliceError {
+    /// Adds an offset value to all slice length related fields.
+    #[inline]
+    pub const fn add_slice_offset(self, offset: usize) -> Self {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => Len(err.add_offset(offset)),
+            Content(err) => Content(err),
+        }
+    }
+
+    /// Returns the [`crate::err::LenError`] if the error is an Len.
+    pub fn len_error(&self) -> Option<&LenError> {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => Some(err),
+            Content(_) => None,
+        }
+    }
+
+    /// Returns the [`crate::err::ipv6_exts::HeaderError`] if the error is an Len.
+    pub fn content(&self) -> Option<&HeaderError> {
+        use HeaderSliceError::*;
+        match self {
+            Len(_) => None,
+            Content(err) => Some(err),
+        }
+    }
+}
+
+impl core::fmt::Display for HeaderSliceError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => err.fmt(f),
+            Content(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderSliceError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{HeaderSliceError::*, *};
+    use crate::{err::Layer, LenSource};
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn add_slice_offset() {
+        assert_eq!(
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3
+            })
+            .add_slice_offset(200),
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 203
+            })
+        );
+        assert_eq!(
+            Content(HeaderError::HopByHopNotAtStart).add_slice_offset(200),
+            Content(HeaderError::HopByHopNotAtStart)
+        );
+    }
+
+    #[test]
+    fn len_error() {
+        assert_eq!(
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3
+            })
+            .len_error(),
+            Some(&LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3
+            })
+        );
+        assert_eq!(Content(HeaderError::HopByHopNotAtStart).len_error(), None);
+    }
+
+    #[test]
+    fn content() {
+        assert_eq!(
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3
+            })
+            .content(),
+            None
+        );
+        assert_eq!(
+            Content(HeaderError::HopByHopNotAtStart).content(),
+            Some(&HeaderError::HopByHopNotAtStart)
+        );
+    }
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::HopByHopNotAtStart;
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Content(HeaderError::HopByHopNotAtStart);
+        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() {
+        {
+            let err = LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Len(err)));
+        }
+        {
+            let err = HeaderError::HopByHopNotAtStart;
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Len(LenError {
+            required_len: 1,
+            layer: Layer::Icmpv4,
+            len: 2,
+            len_source: LenSource::Slice,
+            layer_start_offset: 3
+        })
+        .source()
+        .is_some());
+        assert!(Content(HeaderError::HopByHopNotAtStart).source().is_some());
+    }
+}
diff --git a/src/err/ipv6_exts/header_write_error.rs b/src/err/ipv6_exts/header_write_error.rs
new file mode 100644
index 0000000..0a268dd
--- /dev/null
+++ b/src/err/ipv6_exts/header_write_error.rs
@@ -0,0 +1,140 @@
+#[cfg(feature = "std")]
+use super::ExtsWalkError;
+
+/// Error when writing IPv6 extension headers.
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+#[derive(Debug)]
+pub enum HeaderWriteError {
+    /// IO error encountered while writing.
+    Io(std::io::Error),
+    /// Data was not serializable because of its content.
+    Content(ExtsWalkError),
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl HeaderWriteError {
+    /// Returns a reference to the [`std::io::Error`] if the value is an [`HeaderWriteError::Io`].
+    pub fn io(&self) -> Option<&std::io::Error> {
+        match self {
+            HeaderWriteError::Io(err) => Some(err),
+            HeaderWriteError::Content(_) => None,
+        }
+    }
+
+    /// Returns a reference to the [`crate::err::ipv6_exts::ExtsWalkError`] if the value is an [`HeaderWriteError::Content`].
+    pub fn content(&self) -> Option<&ExtsWalkError> {
+        match self {
+            HeaderWriteError::Io(_) => None,
+            HeaderWriteError::Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl core::fmt::Display for HeaderWriteError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderWriteError::*;
+        match self {
+            Io(err) => err.fmt(f),
+            Content(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderWriteError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderWriteError::*;
+        match self {
+            Io(ref err) => Some(err),
+            Content(ref err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{ExtsWalkError::*, HeaderWriteError::*};
+    use crate::*;
+    use alloc::format;
+    use std::error::Error;
+
+    #[test]
+    fn io() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .io()
+        .is_some());
+        assert!(Content(ExtNotReferenced {
+            missing_ext: IpNumber::AUTHENTICATION_HEADER,
+        })
+        .io()
+        .is_none());
+    }
+
+    #[test]
+    fn content() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .content()
+        .is_none());
+        {
+            let err = ExtNotReferenced {
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            };
+            assert_eq!(Some(&err), Content(err.clone()).content());
+        }
+    }
+
+    #[test]
+    fn debug() {
+        let err = ExtNotReferenced {
+            missing_ext: IpNumber::AUTHENTICATION_HEADER,
+        };
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn fmt() {
+        {
+            let err = std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            );
+            assert_eq!(format!("{}", err), format!("{}", Io(err)));
+        }
+        {
+            let err = ExtNotReferenced {
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            };
+            assert_eq!(format!("{}", Content(err.clone())), format!("{}", err));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .source()
+        .is_some());
+        assert!(Content(ExtNotReferenced {
+            missing_ext: IpNumber::AUTHENTICATION_HEADER,
+        })
+        .source()
+        .is_some());
+    }
+}
diff --git a/src/err/ipv6_exts/mod.rs b/src/err/ipv6_exts/mod.rs
new file mode 100644
index 0000000..8a35885
--- /dev/null
+++ b/src/err/ipv6_exts/mod.rs
@@ -0,0 +1,26 @@
+mod ext_payload_len_error;
+pub use ext_payload_len_error::*;
+
+mod header_error;
+pub use header_error::*;
+
+#[cfg(feature = "std")]
+mod header_limited_read_error;
+#[cfg(feature = "std")]
+pub use header_limited_read_error::*;
+
+#[cfg(feature = "std")]
+mod header_read_error;
+#[cfg(feature = "std")]
+pub use header_read_error::*;
+
+mod exts_walk_error;
+pub use exts_walk_error::*;
+
+mod header_slice_error;
+pub use header_slice_error::*;
+
+#[cfg(feature = "std")]
+mod header_write_error;
+#[cfg(feature = "std")]
+pub use header_write_error::*;
diff --git a/src/err/layer.rs b/src/err/layer.rs
new file mode 100644
index 0000000..16e2ce2
--- /dev/null
+++ b/src/err/layer.rs
@@ -0,0 +1,205 @@
+/// Layers on which an error can occur.
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub enum Layer {
+    /// Error occurred in the Linux Cooked Capture v1 (SLL) header.
+    LinuxSllHeader,
+    /// Error occurred in the ethernet 2 header.
+    Ethernet2Header,
+    /// Payload identified by an ether type number (e.g. after an ethernet 2 header).
+    EtherPayload,
+    /// Error occurred in the vlan header.
+    VlanHeader,
+    /// Error occurred when decoding an IP header (v4 or v6).
+    IpHeader,
+    /// Error occurred in the IPv4 layer.
+    Ipv4Header,
+    /// Error occurred verifying the total length of an IPv4 packet.
+    Ipv4Packet,
+    /// Error occurred in the IP Authentication header.
+    IpAuthHeader,
+    /// Error occurred in the IPv6 layer.
+    Ipv6Header,
+    /// Error occurred verifying the payload length of an IPv6 packet.
+    Ipv6Packet,
+    /// Error occurred while decoding a generic IPv6 extension header.
+    Ipv6ExtHeader,
+    /// Error occurred while decoding "IPv6 Hop-by-Hop Option" extension header.
+    Ipv6HopByHopHeader,
+    /// Error occurred while decoding "IPv6 Destination Options" extension header.
+    Ipv6DestOptionsHeader,
+    /// Error occurred while decoding "IPv6 Routing" extension header.
+    Ipv6RouteHeader,
+    /// Error occurred while decoding an IPv6 fragment header.
+    Ipv6FragHeader,
+    /// Error occurred while decoding an UDP header.
+    UdpHeader,
+    /// Error occurred verifying the length of the UDP payload.
+    UdpPayload,
+    /// Error occurred while decoding a TCP header.
+    TcpHeader,
+    /// Error occurred while parsing an ICMP packet.
+    Icmpv4,
+    /// Error occurred while parsing an ICMP timestamp packet.
+    Icmpv4Timestamp,
+    /// Error occurred while parsing an ICMP timestamp reply packet.
+    Icmpv4TimestampReply,
+    /// Error occurred while parsing an ICMPv6 packet.
+    Icmpv6,
+}
+
+impl Layer {
+    /// String that is used as a title for the error.
+    pub fn error_title(&self) -> &'static str {
+        use Layer::*;
+        match self {
+            LinuxSllHeader => "Linux Cooked Capture v1 Error",
+            Ethernet2Header => "Ethernet 2 Header Error",
+            EtherPayload => "Payload with Ether Type Error",
+            VlanHeader => "VLAN Header Error",
+            IpHeader => "IP Header Error",
+            Ipv4Header => "IPv4 Header Error",
+            Ipv4Packet => "IPv4 Packet Error",
+            IpAuthHeader => "IP Authentication Header Error",
+            Ipv6Header => "IPv6 Header Error",
+            Ipv6Packet => "IPv6 Packet Error",
+            Ipv6ExtHeader => "IPv6 Extension Header Error",
+            Ipv6HopByHopHeader => "IPv6 Hop-by-Hop Option Header Error",
+            Ipv6DestOptionsHeader => "IPv6 Destination Options Header Error",
+            Ipv6RouteHeader => "IPv6 Routing Header Error",
+            Ipv6FragHeader => "IPv6 Fragment Header Error",
+            UdpHeader => "UDP Header Error",
+            UdpPayload => "UDP Payload Error",
+            TcpHeader => "TCP Header Error",
+            Icmpv4 => "ICMP Packet Error",
+            Icmpv4Timestamp => "ICMP Timestamp Error",
+            Icmpv4TimestampReply => "ICMP Timestamp Reply Error",
+            Icmpv6 => "ICMPv6 Packet Error",
+        }
+    }
+}
+
+impl core::fmt::Display for Layer {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use Layer::*;
+        match self {
+            LinuxSllHeader => write!(f, "Linux Cooked Capture v1 header"),
+            Ethernet2Header => write!(f, "Ethernet 2 header"),
+            EtherPayload => write!(f, "Ether type payload"),
+            VlanHeader => write!(f, "VLAN header"),
+            IpHeader => write!(f, "IP header"),
+            Ipv4Header => write!(f, "IPv4 header"),
+            Ipv4Packet => write!(f, "IPv4 packet"),
+            IpAuthHeader => write!(f, "IP Authentication header"),
+            Ipv6Header => write!(f, "IPv6 header"),
+            Ipv6Packet => write!(f, "IPv6 packet"),
+            Ipv6ExtHeader => write!(f, "IPv6 extension header"),
+            Ipv6HopByHopHeader => write!(f, "IPv6 hop-by-hop option header"),
+            Ipv6DestOptionsHeader => write!(f, "IPv6 destination options header"),
+            Ipv6RouteHeader => write!(f, "IPv6 routing header"),
+            Ipv6FragHeader => write!(f, "IPv6 fragment header"),
+            UdpHeader => write!(f, "UDP header"),
+            UdpPayload => write!(f, "UDP payload"),
+            TcpHeader => write!(f, "TCP header"),
+            Icmpv4 => write!(f, "ICMP packet"),
+            Icmpv4Timestamp => write!(f, "ICMP timestamp message"),
+            Icmpv4TimestampReply => write!(f, "ICMP timestamp reply message"),
+            Icmpv6 => write!(f, "ICMPv6 packet"),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::Layer::*;
+    use alloc::format;
+    use std::{
+        cmp::Ordering,
+        collections::hash_map::DefaultHasher,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        assert_eq!("Ethernet2Header", format!("{:?}", Ethernet2Header));
+    }
+
+    #[test]
+    fn clone_eq_hash_ord() {
+        let layer = Ethernet2Header;
+        assert_eq!(layer, layer.clone());
+        let hash_a = {
+            let mut hasher = DefaultHasher::new();
+            layer.hash(&mut hasher);
+            hasher.finish()
+        };
+        let hash_b = {
+            let mut hasher = DefaultHasher::new();
+            layer.clone().hash(&mut hasher);
+            hasher.finish()
+        };
+        assert_eq!(hash_a, hash_b);
+        assert_eq!(Ordering::Equal, layer.cmp(&layer));
+        assert_eq!(Some(Ordering::Equal), layer.partial_cmp(&layer));
+    }
+
+    #[test]
+    fn error_title() {
+        let tests = [
+            (Ethernet2Header, "Ethernet 2 Header Error"),
+            (VlanHeader, "VLAN Header Error"),
+            (IpHeader, "IP Header Error"),
+            (Ipv4Header, "IPv4 Header Error"),
+            (Ipv4Packet, "IPv4 Packet Error"),
+            (IpAuthHeader, "IP Authentication Header Error"),
+            (Ipv6Header, "IPv6 Header Error"),
+            (Ipv6Packet, "IPv6 Packet Error"),
+            (Ipv6ExtHeader, "IPv6 Extension Header Error"),
+            (Ipv6HopByHopHeader, "IPv6 Hop-by-Hop Option Header Error"),
+            (
+                Ipv6DestOptionsHeader,
+                "IPv6 Destination Options Header Error",
+            ),
+            (Ipv6RouteHeader, "IPv6 Routing Header Error"),
+            (Ipv6FragHeader, "IPv6 Fragment Header Error"),
+            (UdpHeader, "UDP Header Error"),
+            (UdpPayload, "UDP Payload Error"),
+            (TcpHeader, "TCP Header Error"),
+            (Icmpv4, "ICMP Packet Error"),
+            (Icmpv4Timestamp, "ICMP Timestamp Error"),
+            (Icmpv4TimestampReply, "ICMP Timestamp Reply Error"),
+            (Icmpv6, "ICMPv6 Packet Error"),
+        ];
+        for test in tests {
+            assert_eq!(test.0.error_title(), test.1);
+        }
+    }
+
+    #[test]
+    fn fmt() {
+        let tests = [
+            (Ethernet2Header, "Ethernet 2 header"),
+            (VlanHeader, "VLAN header"),
+            (IpHeader, "IP header"),
+            (Ipv4Header, "IPv4 header"),
+            (Ipv4Packet, "IPv4 packet"),
+            (IpAuthHeader, "IP Authentication header"),
+            (Ipv6Header, "IPv6 header"),
+            (Ipv6Packet, "IPv6 packet"),
+            (Ipv6ExtHeader, "IPv6 extension header"),
+            (Ipv6HopByHopHeader, "IPv6 hop-by-hop option header"),
+            (Ipv6DestOptionsHeader, "IPv6 destination options header"),
+            (Ipv6RouteHeader, "IPv6 routing header"),
+            (Ipv6FragHeader, "IPv6 fragment header"),
+            (UdpHeader, "UDP header"),
+            (UdpPayload, "UDP payload"),
+            (TcpHeader, "TCP header"),
+            (Icmpv4, "ICMP packet"),
+            (Icmpv4Timestamp, "ICMP timestamp message"),
+            (Icmpv4TimestampReply, "ICMP timestamp reply message"),
+            (Icmpv6, "ICMPv6 packet"),
+        ];
+        for test in tests {
+            assert_eq!(format!("{}", test.0), test.1);
+        }
+    }
+}
diff --git a/src/err/len_error.rs b/src/err/len_error.rs
new file mode 100644
index 0000000..bf60e15
--- /dev/null
+++ b/src/err/len_error.rs
@@ -0,0 +1,324 @@
+use crate::{err::Layer, LenSource};
+
+/// Error when different lengths are conflicting with each other (e.g. not
+/// enough data in a slice to decode a header).
+///
+/// This error is triggered whenever there is not enough data to decode
+/// an element (e.g. if a slice is too small to decode an header) or
+/// if a length that is inhered from an upper layer is too big for the
+/// lower layer (e.g. length inherited from an IP header is too big to
+/// be used as an ICMP packet length).
+///
+/// When the error is caused by not enough data being available
+/// `required_len > len` must be true. While when the length from
+/// the upper layer is too big for the lower layer the inverse
+/// (`required_len < len`) must be true.
+///
+/// # Examples:
+///
+/// An example for an error that could be returned when there is not enough
+/// data available to decode an UDP header would be:
+///
+/// ```
+/// use etherparse::*;
+///
+/// err::LenError{
+///     // Expected to have at least the length of an UDP header present:
+///     required_len: UdpHeader::LEN,
+///     // Could not decode the UDP header:
+///     layer: err::Layer::UdpHeader,
+///     // There was only 1 byte left (not enough for an UDP header):
+///     len: 1,
+///     // The provided length was determined by the total length field in the
+///     // IPv4 header:
+///     len_source: LenSource::Ipv4HeaderTotalLen,
+///     // Offset in bytes from the start of decoding (ethernet in this) case
+///     // to the expected UDP header start:
+///     layer_start_offset: Ethernet2Header::LEN + Ipv4Header::MIN_LEN
+/// };
+/// ```
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub struct LenError {
+    /// Expected minimum or maximum length conflicting with the
+    /// `len` value.
+    pub required_len: usize,
+
+    /// Length limiting or exceeding the required length.
+    pub len: usize,
+
+    /// Source of the outer length (e.g. Slice or a length specified by
+    /// an upper level protocol).
+    pub len_source: LenSource,
+
+    /// Layer in which the length error was encountered.
+    pub layer: Layer,
+
+    /// Offset from the start of the parsed data to the layer where the
+    /// length error occurred.
+    pub layer_start_offset: usize,
+}
+
+impl LenError {
+    /// Adds an offset value to the `layer_start_offset` field.
+    #[inline]
+    pub const fn add_offset(self, offset: usize) -> Self {
+        LenError {
+            required_len: self.required_len,
+            layer: self.layer,
+            len: self.len,
+            len_source: self.len_source,
+            layer_start_offset: self.layer_start_offset + offset,
+        }
+    }
+}
+
+impl core::fmt::Display for LenError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        let len_source: &'static str = {
+            use LenSource::*;
+            match self.len_source {
+                Slice => "slice length",
+                Ipv4HeaderTotalLen => "length calculated from the IPv4 header 'total length' field",
+                Ipv6HeaderPayloadLen => {
+                    "length calculated from the IPv6 header 'payload length' field"
+                }
+                UdpHeaderLen => "length calculated from the UDP header 'length' field",
+                TcpHeaderLen => "length calculated from the TCP header 'length' field",
+            }
+        };
+
+        if self.required_len > self.len {
+            if self.layer_start_offset > 0 {
+                write!(
+                    f,
+                    "{}: Not enough data to decode '{}'. {} byte(s) would be required, but only {} byte(s) are available based on the {} ('{}' starts at overall parsed byte {}).",
+                    self.layer.error_title(),
+                    self.layer,
+                    self.required_len,
+                    self.len,
+                    len_source,
+                    self.layer,
+                    self.layer_start_offset
+                )
+            } else {
+                write!(
+                    f,
+                    "{}: Not enough data to decode '{}'. {} byte(s) would be required, but only {} byte(s) are available based on the {}.",
+                    self.layer.error_title(),
+                    self.layer,
+                    self.required_len,
+                    self.len,
+                    len_source
+                )
+            }
+        } else if self.layer_start_offset > 0 {
+            write!(
+                f,
+                "{}: Length of {} byte(s) is too big for an '{}' (maximum is {} bytes). The {} was used to determine the length ('{}' starts at overall parsed byte {}).",
+                self.layer.error_title(),
+                self.len,
+                self.layer,
+                self.required_len,
+                len_source,
+                self.layer,
+                self.layer_start_offset
+            )
+        } else {
+            write!(
+                f,
+                "{}: Length of {} byte(s) is too big for an '{}' (maximum is {} bytes). The {} was used to determine the length.",
+                self.layer.error_title(),
+                self.len,
+                self.layer,
+                self.required_len,
+                len_source
+            )
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for LenError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        None
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn add_offset() {
+        assert_eq!(
+            LenError {
+                required_len: 2,
+                layer: Layer::Icmpv4,
+                len: 1,
+                len_source: LenSource::Slice,
+                layer_start_offset: 20,
+            }
+            .add_offset(100),
+            LenError {
+                required_len: 2,
+                layer: Layer::Icmpv4,
+                len: 1,
+                len_source: LenSource::Slice,
+                layer_start_offset: 120,
+            }
+        );
+    }
+
+    #[test]
+    fn debug() {
+        assert_eq!(
+            format!(
+                "{:?}",
+                LenError {
+                    required_len: 2,
+                    layer: Layer::Ipv4Header,
+                    len: 1,
+                    len_source: LenSource::Slice,
+                    layer_start_offset: 0
+                }
+            ),
+            format!(
+                "LenError {{ required_len: {:?}, len: {:?}, len_source: {:?}, layer: {:?}, layer_start_offset: {:?} }}",
+                2, 1, LenSource::Slice, Layer::Ipv4Header, 0
+            ),
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = LenError {
+            required_len: 2,
+            layer: Layer::Icmpv4,
+            len: 1,
+            len_source: LenSource::Slice,
+            layer_start_offset: 20,
+        };
+        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() {
+        // len sources based tests (not enough data)
+        {
+            use crate::LenSource::*;
+            let len_source_tests = [
+                (Slice, "IPv4 Header Error: Not enough data to decode 'IPv4 header'. 2 byte(s) would be required, but only 1 byte(s) are available based on the slice length."),
+                (Ipv4HeaderTotalLen, "IPv4 Header Error: Not enough data to decode 'IPv4 header'. 2 byte(s) would be required, but only 1 byte(s) are available based on the length calculated from the IPv4 header 'total length' field."),
+                (Ipv6HeaderPayloadLen, "IPv4 Header Error: Not enough data to decode 'IPv4 header'. 2 byte(s) would be required, but only 1 byte(s) are available based on the length calculated from the IPv6 header 'payload length' field."),
+                (UdpHeaderLen, "IPv4 Header Error: Not enough data to decode 'IPv4 header'. 2 byte(s) would be required, but only 1 byte(s) are available based on the length calculated from the UDP header 'length' field."),
+                (TcpHeaderLen, "IPv4 Header Error: Not enough data to decode 'IPv4 header'. 2 byte(s) would be required, but only 1 byte(s) are available based on the length calculated from the TCP header 'length' field."),
+            ];
+
+            for test in len_source_tests {
+                assert_eq!(
+                    test.1,
+                    format!(
+                        "{}",
+                        LenError {
+                            required_len: 2,
+                            layer: Layer::Ipv4Header,
+                            len: 1,
+                            len_source: test.0,
+                            layer_start_offset: 0
+                        }
+                    )
+                );
+            }
+        }
+
+        // start offset based test
+        assert_eq!(
+            "IPv4 Header Error: Not enough data to decode 'IPv4 header'. 2 byte(s) would be required, but only 1 byte(s) are available based on the slice length ('IPv4 header' starts at overall parsed byte 4).",
+            format!(
+                "{}",
+                LenError{
+                    required_len: 2,
+                    len: 1,
+                    len_source: LenSource::Slice,
+                    layer: Layer::Ipv4Header,
+                    layer_start_offset: 4
+                }
+            )
+        );
+
+        // len sources based tests (length too big)
+        {
+            use crate::LenSource::*;
+            let len_source_tests = [
+                (Slice, "IPv4 Header Error: Length of 2 byte(s) is too big for an 'IPv4 header' (maximum is 1 bytes). The slice length was used to determine the length."),
+                (Ipv4HeaderTotalLen, "IPv4 Header Error: Length of 2 byte(s) is too big for an 'IPv4 header' (maximum is 1 bytes). The length calculated from the IPv4 header 'total length' field was used to determine the length."),
+                (Ipv6HeaderPayloadLen, "IPv4 Header Error: Length of 2 byte(s) is too big for an 'IPv4 header' (maximum is 1 bytes). The length calculated from the IPv6 header 'payload length' field was used to determine the length."),
+                (UdpHeaderLen, "IPv4 Header Error: Length of 2 byte(s) is too big for an 'IPv4 header' (maximum is 1 bytes). The length calculated from the UDP header 'length' field was used to determine the length."),
+                (TcpHeaderLen, "IPv4 Header Error: Length of 2 byte(s) is too big for an 'IPv4 header' (maximum is 1 bytes). The length calculated from the TCP header 'length' field was used to determine the length."),
+            ];
+
+            for test in len_source_tests {
+                assert_eq!(
+                    test.1,
+                    format!(
+                        "{}",
+                        LenError {
+                            required_len: 1,
+                            layer: Layer::Ipv4Header,
+                            len: 2,
+                            len_source: test.0,
+                            layer_start_offset: 0
+                        }
+                    )
+                );
+            }
+        }
+
+        // start offset based test
+        assert_eq!(
+            "IPv4 Header Error: Length of 2 byte(s) is too big for an 'IPv4 header' (maximum is 1 bytes). The slice length was used to determine the length ('IPv4 header' starts at overall parsed byte 4).",
+            format!(
+                "{}",
+                LenError{
+                    required_len: 1,
+                    len: 2,
+                    len_source: LenSource::Slice,
+                    layer: Layer::Ipv4Header,
+                    layer_start_offset: 4
+                }
+            )
+        );
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(LenError {
+            required_len: 0,
+            len: 0,
+            len_source: LenSource::Slice,
+            layer: Layer::Ipv4Header,
+            layer_start_offset: 0
+        }
+        .source()
+        .is_none());
+    }
+}
diff --git a/src/err/linux_sll/header_error.rs b/src/err/linux_sll/header_error.rs
new file mode 100644
index 0000000..2fb504d
--- /dev/null
+++ b/src/err/linux_sll/header_error.rs
@@ -0,0 +1,99 @@
+use crate::ArpHardwareId;
+
+/// Errors in an Linux Cooked Capture header encountered while decoding it.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeaderError {
+    /// Error when the "packet byte" field is not one of the known ones
+    UnsupportedPacketTypeField {
+        // The unexpected packet type number in the SLL header
+        packet_type: u16,
+    },
+    /// Error when the arp hardware type field is not one of the known ones
+    UnsupportedArpHardwareId { arp_hardware_type: ArpHardwareId },
+}
+
+impl core::fmt::Display for HeaderError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderError::*;
+        match self {
+            UnsupportedPacketTypeField { packet_type } => write!(f, "Linux cooked capture v1 (SLL) Header Error: Encountered '{}' as the packet type, but its not supported.", packet_type),
+            UnsupportedArpHardwareId { arp_hardware_type } => write!(f, "Linux cooked capture v1 (SLL)  Header Error:  Encountered '{}' as the ARP harware type, but its not supported.", arp_hardware_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)> {
+        use HeaderError::*;
+        match self {
+            UnsupportedPacketTypeField { packet_type: _ } => None,
+            UnsupportedArpHardwareId {
+                arp_hardware_type: _,
+            } => 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!(
+            "UnsupportedPacketTypeField { packet_type: 6 }",
+            format!("{:?}", UnsupportedPacketTypeField { packet_type: 6 })
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = HeaderError::UnsupportedPacketTypeField { packet_type: 6 };
+        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!(
+            "Linux cooked capture v1 (SLL) Header Error: Encountered '6' as the packet type, but its not supported.",
+            format!("{}", UnsupportedPacketTypeField{ packet_type: 6 })
+        );
+        assert_eq!(
+            "Linux cooked capture v1 (SLL)  Header Error:  Encountered '1 (Ethernet 10Mbps)' as the ARP harware type, but its not supported.",
+            format!("{}", UnsupportedArpHardwareId{ arp_hardware_type: ArpHardwareId::ETHER })
+        );
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        let values = [
+            UnsupportedPacketTypeField { packet_type: 6 },
+            UnsupportedArpHardwareId {
+                arp_hardware_type: ArpHardwareId::ETHER,
+            },
+        ];
+        for v in values {
+            assert!(v.source().is_none());
+        }
+    }
+}
diff --git a/src/err/linux_sll/header_read_error.rs b/src/err/linux_sll/header_read_error.rs
new file mode 100644
index 0000000..a7d1ebc
--- /dev/null
+++ b/src/err/linux_sll/header_read_error.rs
@@ -0,0 +1,146 @@
+use super::HeaderError;
+
+/// Error when decoding Linux Cooked Capture v1 (SLL) headers via a
+/// `std::io::Read` source.
+///
+/// Requires crate feature `std`.
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+#[derive(Debug)]
+pub enum HeaderReadError {
+    /// IO error was encountered while reading header.
+    Io(std::io::Error),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl HeaderReadError {
+    /// Returns the `std::io::Error` value if the `HeaderReadError` is `Io`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn io_error(self) -> Option<std::io::Error> {
+        use HeaderReadError::*;
+        match self {
+            Io(value) => Some(value),
+            _ => None,
+        }
+    }
+
+    /// Returns the `err::linux_sll::HeaderError` value if the `HeaderReadError` is `Content`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn content_error(self) -> Option<HeaderError> {
+        use HeaderReadError::*;
+        match self {
+            Content(value) => Some(value),
+            _ => None,
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+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, "Linux Cooked Capture v1 (SLL) Header IO Error: {}", err),
+            Content(value) => value.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderReadError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderReadError::*;
+        match self {
+            Io(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(all(test, feature = "std"))]
+mod test {
+    use super::{HeaderReadError::*, *};
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::UnsupportedPacketTypeField { packet_type: 1 };
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn fmt() {
+        {
+            let err = std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            );
+            assert_eq!(
+                format!("Linux Cooked Capture v1 (SLL) Header IO Error: {}", err),
+                format!("{}", Io(err))
+            );
+        }
+        {
+            let err = HeaderError::UnsupportedPacketTypeField { packet_type: 1 };
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[test]
+    fn source() {
+        use std::error::Error;
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .source()
+        .is_some());
+        assert!(
+            Content(HeaderError::UnsupportedPacketTypeField { packet_type: 1 })
+                .source()
+                .is_some()
+        );
+    }
+
+    #[test]
+    fn io_error() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .io_error()
+        .is_some());
+        assert!(
+            Content(HeaderError::UnsupportedPacketTypeField { packet_type: 1 })
+                .io_error()
+                .is_none()
+        );
+    }
+
+    #[test]
+    fn content_error() {
+        assert_eq!(
+            None,
+            Io(std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            ))
+            .content_error()
+        );
+        {
+            let err = HeaderError::UnsupportedPacketTypeField { packet_type: 1 };
+            assert_eq!(Some(err.clone()), Content(err.clone()).content_error());
+        }
+    }
+}
diff --git a/src/err/linux_sll/header_slice_error.rs b/src/err/linux_sll/header_slice_error.rs
new file mode 100644
index 0000000..134c4a5
--- /dev/null
+++ b/src/err/linux_sll/header_slice_error.rs
@@ -0,0 +1,148 @@
+use super::HeaderError;
+use crate::err::LenError;
+
+/// Error when decoding Linux Cooked Capture v1 (SLL) header from a slice.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeaderSliceError {
+    /// Error when an length error is encountered (e.g. unexpected
+    /// end of slice).
+    Len(LenError),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+impl HeaderSliceError {
+    /// Adds an offset value to all slice length related fields.
+    #[inline]
+    pub const fn add_slice_offset(self, offset: usize) -> Self {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => Len(err.add_offset(offset)),
+            Content(err) => Content(err),
+        }
+    }
+}
+
+impl core::fmt::Display for HeaderSliceError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => err.fmt(f),
+            Content(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderSliceError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{HeaderSliceError::*, *};
+    use crate::{err::Layer, LenSource};
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn add_slice_offset() {
+        assert_eq!(
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::LinuxSllHeader,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3
+            })
+            .add_slice_offset(200),
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::LinuxSllHeader,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 203
+            })
+        );
+        assert_eq!(
+            Content(HeaderError::UnsupportedPacketTypeField { packet_type: 0 })
+                .add_slice_offset(200),
+            Content(HeaderError::UnsupportedPacketTypeField { packet_type: 0 })
+        );
+    }
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::UnsupportedPacketTypeField { packet_type: 0 };
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Content(HeaderError::UnsupportedPacketTypeField { packet_type: 0 });
+        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() {
+        {
+            let err = LenError {
+                required_len: 1,
+                layer: Layer::LinuxSllHeader,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Len(err)));
+        }
+        {
+            let err = HeaderError::UnsupportedPacketTypeField { packet_type: 0 };
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Len(LenError {
+            required_len: 1,
+            layer: Layer::LinuxSllHeader,
+            len: 2,
+            len_source: LenSource::Slice,
+            layer_start_offset: 3
+        })
+        .source()
+        .is_some());
+        assert!(
+            Content(HeaderError::UnsupportedPacketTypeField { packet_type: 0 })
+                .source()
+                .is_some()
+        );
+    }
+}
diff --git a/src/err/linux_sll/mod.rs b/src/err/linux_sll/mod.rs
new file mode 100644
index 0000000..bf4f0e8
--- /dev/null
+++ b/src/err/linux_sll/mod.rs
@@ -0,0 +1,10 @@
+mod header_error;
+pub use header_error::*;
+
+#[cfg(feature = "std")]
+mod header_read_error;
+#[cfg(feature = "std")]
+pub use header_read_error::*;
+
+mod header_slice_error;
+pub use header_slice_error::*;
diff --git a/src/err/mod.rs b/src/err/mod.rs
new file mode 100644
index 0000000..27d97d1
--- /dev/null
+++ b/src/err/mod.rs
@@ -0,0 +1,37 @@
+pub mod double_vlan;
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod io;
+pub mod ip;
+pub mod ip_auth;
+pub mod ip_exts;
+pub mod ipv4;
+pub mod ipv4_exts;
+pub mod ipv6;
+pub mod ipv6_exts;
+pub mod linux_sll;
+pub mod packet;
+pub mod tcp;
+
+mod value_type;
+pub use value_type::*;
+
+mod from_slice_error;
+pub use from_slice_error::*;
+
+mod layer;
+pub use layer::*;
+
+mod len_error;
+pub use len_error::*;
+
+mod value_too_big_error;
+pub use value_too_big_error::*;
+
+#[cfg(feature = "std")]
+mod read_error;
+#[cfg(feature = "std")]
+pub use read_error::*;
+
+mod slice_write_space_error;
+pub use slice_write_space_error::*;
diff --git a/src/err/packet/build_write_error.rs b/src/err/packet/build_write_error.rs
new file mode 100644
index 0000000..67251c5
--- /dev/null
+++ b/src/err/packet/build_write_error.rs
@@ -0,0 +1,264 @@
+#[cfg(feature = "std")]
+use crate::err::{ipv4_exts, ipv6_exts, ValueTooBigError};
+
+/// Error while writing packet
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+#[derive(Debug)]
+pub enum BuildWriteError {
+    /// IO error while writing packet.
+    Io(std::io::Error),
+
+    /// Error if the length of the payload is too
+    /// big to be representable by the length fields.
+    PayloadLen(ValueTooBigError<usize>),
+
+    /// Error if the IPv4 extensions can not be serialized
+    /// because of internal consistency errors (i.e. a header
+    /// is never).
+    Ipv4Exts(ipv4_exts::ExtsWalkError),
+
+    /// Error if the IPv6 extensions can not be serialized
+    /// because of internal consistency errors.
+    Ipv6Exts(ipv6_exts::ExtsWalkError),
+
+    /// Error if ICMPv6 is packaged in an IPv4 packet (it is undefined
+    /// how to calculate the checksum).
+    Icmpv6InIpv4,
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl BuildWriteError {
+    /// Returns the [`std::io::Error`] value if the `BuildWriteError` is an `Io`.
+    /// Otherwise `None` is returned.
+    pub fn io(&self) -> Option<&std::io::Error> {
+        match self {
+            BuildWriteError::Io(err) => Some(err),
+            _ => None,
+        }
+    }
+
+    /// Returns the [`crate::err::ValueTooBigError`] value if the
+    /// `BuildWriteError` is a `PayloadLen`. Otherwise `None` is returned.
+    pub fn payload_len(&self) -> Option<&ValueTooBigError<usize>> {
+        match self {
+            BuildWriteError::PayloadLen(err) => Some(err),
+            _ => None,
+        }
+    }
+
+    /// Returns the [`crate::err::ipv4_exts::ExtsWalkError`] value if the
+    /// `BuildWriteError` is a `Ipv4Exts`. Otherwise `None` is returned.
+    pub fn ipv4_exts(&self) -> Option<&ipv4_exts::ExtsWalkError> {
+        match self {
+            BuildWriteError::Ipv4Exts(err) => Some(err),
+            _ => None,
+        }
+    }
+
+    /// Returns the [`crate::err::ipv6_exts::ExtsWalkError`] value if the
+    /// `BuildWriteError` is a `Ipv6Exts`. Otherwise `None` is returned.
+    pub fn ipv6_exts(&self) -> Option<&ipv6_exts::ExtsWalkError> {
+        match self {
+            BuildWriteError::Ipv6Exts(err) => Some(err),
+            _ => None,
+        }
+    }
+
+    /// Returns true if the `BuildWriteError` is a `Icmpv6InIpv4`.
+    pub fn is_icmpv6_in_ipv4(&self) -> bool {
+        matches!(self, BuildWriteError::Icmpv6InIpv4)
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl core::fmt::Display for BuildWriteError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use BuildWriteError::*;
+        match self {
+            Io(err) => err.fmt(f),
+            PayloadLen(err) => err.fmt(f),
+            Ipv4Exts(err) => err.fmt(f),
+            Ipv6Exts(err) => err.fmt(f),
+            Icmpv6InIpv4 => write!(f, "Error: ICMPv6 can not be combined with an IPv4 headers (checksum can not be calculated)."),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for BuildWriteError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use BuildWriteError::*;
+        match self {
+            Io(ref err) => Some(err),
+            PayloadLen(ref err) => Some(err),
+            Ipv4Exts(err) => Some(err),
+            Ipv6Exts(err) => Some(err),
+            Icmpv6InIpv4 => None,
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{BuildWriteError::*, *};
+    use crate::{err::ValueType, *};
+    use alloc::format;
+    use std::error::Error;
+
+    #[test]
+    fn io() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .io()
+        .is_some());
+        assert!(Ipv4Exts(ipv4_exts::ExtsWalkError::ExtNotReferenced {
+            missing_ext: IpNumber::AUTHENTICATION_HEADER,
+        })
+        .io()
+        .is_none());
+    }
+
+    #[test]
+    fn payload_len() {
+        {
+            let err = ValueTooBigError {
+                actual: 3,
+                max_allowed: 2,
+                value_type: ValueType::Ipv4PayloadLength,
+            };
+            assert_eq!(Some(&err), PayloadLen(err.clone()).payload_len());
+        }
+        {
+            let err = ipv4_exts::ExtsWalkError::ExtNotReferenced {
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            };
+            assert_eq!(None, Ipv4Exts(err.clone()).payload_len());
+        }
+    }
+
+    #[test]
+    fn ipv4_exts() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .ipv4_exts()
+        .is_none());
+        {
+            let err = ipv4_exts::ExtsWalkError::ExtNotReferenced {
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            };
+            assert_eq!(Some(&err), Ipv4Exts(err.clone()).ipv4_exts());
+        }
+    }
+
+    #[test]
+    fn ipv6_exts() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .ipv6_exts()
+        .is_none());
+        {
+            let err = ipv6_exts::ExtsWalkError::ExtNotReferenced {
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            };
+            assert_eq!(Some(&err), Ipv6Exts(err.clone()).ipv6_exts());
+        }
+    }
+
+    #[test]
+    fn is_icmpv6_in_ipv4() {
+        assert_eq!(
+            false,
+            Io(std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            ))
+            .is_icmpv6_in_ipv4()
+        );
+        assert!(Icmpv6InIpv4.is_icmpv6_in_ipv4());
+    }
+
+    #[test]
+    fn debug() {
+        let err = ipv4_exts::ExtsWalkError::ExtNotReferenced {
+            missing_ext: IpNumber::AUTHENTICATION_HEADER,
+        };
+        assert_eq!(
+            format!("Ipv4Exts({:?})", err.clone()),
+            format!("{:?}", Ipv4Exts(err))
+        );
+    }
+
+    #[test]
+    fn fmt() {
+        {
+            let err = std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            );
+            assert_eq!(format!("{}", err), format!("{}", Io(err)));
+        }
+        {
+            let err = ValueTooBigError {
+                actual: 3,
+                max_allowed: 2,
+                value_type: ValueType::Ipv4PayloadLength,
+            };
+            assert_eq!(format!("{}", err), format!("{}", PayloadLen(err.clone())));
+        }
+        {
+            let err = ipv4_exts::ExtsWalkError::ExtNotReferenced {
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            };
+            assert_eq!(format!("{}", err), format!("{}", Ipv4Exts(err.clone())));
+        }
+        {
+            let err = ipv6_exts::ExtsWalkError::ExtNotReferenced {
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            };
+            assert_eq!(format!("{}", err), format!("{}", Ipv6Exts(err.clone())));
+        }
+        assert_eq!(
+            "Error: ICMPv6 can not be combined with an IPv4 headers (checksum can not be calculated).",
+            format!("{}", Icmpv6InIpv4)
+        );
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .source()
+        .is_some());
+        assert!(PayloadLen(ValueTooBigError {
+            actual: 3,
+            max_allowed: 2,
+            value_type: ValueType::Ipv4PayloadLength,
+        })
+        .source()
+        .is_some());
+        assert!(Ipv4Exts(ipv4_exts::ExtsWalkError::ExtNotReferenced {
+            missing_ext: IpNumber::AUTHENTICATION_HEADER,
+        })
+        .source()
+        .is_some());
+        assert!(Ipv6Exts(ipv6_exts::ExtsWalkError::ExtNotReferenced {
+            missing_ext: IpNumber::AUTHENTICATION_HEADER,
+        })
+        .source()
+        .is_some());
+        assert!(Icmpv6InIpv4.source().is_none());
+    }
+}
diff --git a/src/err/packet/mod.rs b/src/err/packet/mod.rs
new file mode 100644
index 0000000..fd8bacb
--- /dev/null
+++ b/src/err/packet/mod.rs
@@ -0,0 +1,10 @@
+#[cfg(feature = "std")]
+mod build_write_error;
+#[cfg(feature = "std")]
+pub use build_write_error::*;
+
+mod slice_error;
+pub use slice_error::*;
+
+mod transport_checksum_error;
+pub use transport_checksum_error::*;
diff --git a/src/err/packet/slice_error.rs b/src/err/packet/slice_error.rs
new file mode 100644
index 0000000..1326156
--- /dev/null
+++ b/src/err/packet/slice_error.rs
@@ -0,0 +1,221 @@
+use crate::*;
+
+/// Error when slicing an packet from downwards (both
+/// starting from ethernet or ip layer downwards).
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum SliceError {
+    /// Length related errors (e.g. not enough data in slice).
+    Len(err::LenError),
+    /// Error when decoding an Linux SLL header.
+    LinuxSll(err::linux_sll::HeaderError),
+    /// Error when decoding starting at an IP header (v4 or v6).
+    Ip(err::ip::HeaderError),
+    /// Error when decoding an IPv4 header.
+    Ipv4(err::ipv4::HeaderError),
+    /// Error when decoding an IPv6 header.
+    Ipv6(err::ipv6::HeaderError),
+    /// Error when decoding an IPv4 extension header.
+    Ipv4Exts(err::ip_auth::HeaderError),
+    /// Error when decoding an IPv6 extension header.
+    Ipv6Exts(err::ipv6_exts::HeaderError),
+    /// Error when decoding a TCP header.
+    Tcp(err::tcp::HeaderError),
+}
+
+impl core::fmt::Display for SliceError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use SliceError::*;
+
+        match self {
+            Len(err) => err.fmt(f),
+            LinuxSll(err) => err.fmt(f),
+            Ip(err) => err.fmt(f),
+            Ipv4(err) => err.fmt(f),
+            Ipv6(err) => err.fmt(f),
+            Ipv4Exts(err) => err.fmt(f),
+            Ipv6Exts(err) => err.fmt(f),
+            Tcp(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for SliceError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use SliceError::*;
+        match self {
+            Len(err) => Some(err),
+            LinuxSll(err) => Some(err),
+            Ip(err) => Some(err),
+            Ipv4(err) => Some(err),
+            Ipv6(err) => Some(err),
+            Ipv4Exts(err) => Some(err),
+            Ipv6Exts(err) => Some(err),
+            Tcp(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{SliceError::*, *};
+    use crate::err::Layer;
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        let err = err::ipv4::HeaderError::UnexpectedVersion { version_number: 1 };
+        assert_eq!(
+            format!("Ipv4({:?})", err.clone()),
+            format!("{:?}", Ipv4(err))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Ipv4(err::ipv4::HeaderError::UnexpectedVersion { version_number: 1 });
+        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() {
+        // Len
+        {
+            let err = err::LenError {
+                required_len: 2,
+                len: 1,
+                len_source: LenSource::Slice,
+                layer: Layer::TcpHeader,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", err), format!("{}", Len(err)));
+        }
+
+        // Linux SLL Header
+        {
+            let err = err::linux_sll::HeaderError::UnsupportedArpHardwareId {
+                arp_hardware_type: ArpHardwareId::ADAPT,
+            };
+            assert_eq!(
+                format!("{}", err),
+                format!("{}", err::packet::SliceError::LinuxSll(err))
+            );
+        }
+
+        // IpHeader
+        {
+            let err = err::ip::HeaderError::UnsupportedIpVersion { version_number: 1 };
+            assert_eq!(
+                format!("{}", err),
+                format!("{}", err::packet::SliceError::Ip(err))
+            );
+        }
+
+        // Ipv4Header
+        {
+            let err = err::ipv4::HeaderError::UnexpectedVersion { version_number: 1 };
+            assert_eq!(format!("{}", err), format!("{}", Ipv4(err)));
+        }
+
+        // Ipv6Header
+        {
+            let err = err::ipv6::HeaderError::UnexpectedVersion { version_number: 1 };
+            assert_eq!(format!("{}", err), format!("{}", Ipv6(err)));
+        }
+
+        // Ipv4ExtHeader
+        {
+            let err = err::ip_auth::HeaderError::ZeroPayloadLen;
+            assert_eq!(format!("{}", err), format!("{}", Ipv4Exts(err)));
+        }
+
+        // Ipv6ExtHeader
+        {
+            let err = err::ipv6_exts::HeaderError::HopByHopNotAtStart;
+            assert_eq!(format!("{}", err), format!("{}", Ipv6Exts(err)));
+        };
+
+        // TcpHeader
+        {
+            let err = err::tcp::HeaderError::DataOffsetTooSmall { data_offset: 1 };
+            assert_eq!(format!("{}", err), format!("{}", Tcp(err)));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        // Len
+        {
+            let err = err::LenError {
+                required_len: 2,
+                len: 1,
+                len_source: LenSource::Slice,
+                layer: Layer::TcpHeader,
+                layer_start_offset: 3,
+            };
+            assert!(Len(err).source().is_some());
+        }
+
+        // IpHeaders
+        {
+            let err = err::linux_sll::HeaderError::UnsupportedArpHardwareId {
+                arp_hardware_type: ArpHardwareId::ETHER,
+            };
+            assert!(LinuxSll(err).source().is_some());
+        }
+
+        // IpHeaders
+        {
+            let err = err::ip::HeaderError::UnsupportedIpVersion { version_number: 1 };
+            assert!(Ip(err).source().is_some());
+        }
+
+        // Ipv4Header
+        {
+            let err = err::ipv4::HeaderError::UnexpectedVersion { version_number: 1 };
+            assert!(Ipv4(err).source().is_some());
+        }
+
+        // Ipv6Header
+        {
+            let err = err::ipv6::HeaderError::UnexpectedVersion { version_number: 1 };
+            assert!(Ipv6(err).source().is_some());
+        }
+
+        // Ipv4ExtHeader
+        {
+            let err = err::ip_auth::HeaderError::ZeroPayloadLen;
+            assert!(Ipv4Exts(err).source().is_some());
+        }
+
+        // Ipv6ExtHeader
+        {
+            let err = err::ipv6_exts::HeaderError::HopByHopNotAtStart;
+            assert!(Ipv6Exts(err).source().is_some());
+        };
+
+        // TcpHeader
+        {
+            let err = err::tcp::HeaderError::DataOffsetTooSmall { data_offset: 1 };
+            assert!(Tcp(err).source().is_some());
+        }
+    }
+}
diff --git a/src/err/packet/transport_checksum_error.rs b/src/err/packet/transport_checksum_error.rs
new file mode 100644
index 0000000..6d698fc
--- /dev/null
+++ b/src/err/packet/transport_checksum_error.rs
@@ -0,0 +1,104 @@
+use crate::err::ValueTooBigError;
+
+/// Error while calculating the checksum in a transport header.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum TransportChecksumError {
+    /// Error if the length of the payload is too
+    /// big to be representable by the length fields.
+    PayloadLen(ValueTooBigError<usize>),
+
+    /// Error when an Icmpv6 payload is found in an IPv4 packet.
+    Icmpv6InIpv4,
+}
+
+impl core::fmt::Display for TransportChecksumError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use TransportChecksumError::*;
+        match self {
+            PayloadLen(err) => err.fmt(f),
+            Icmpv6InIpv4 => write!(f, "Error: ICMPv6 can not be combined with an IPv4 headers (checksum can not be calculated)."),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for TransportChecksumError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use TransportChecksumError::*;
+        match self {
+            PayloadLen(err) => Some(err),
+            Icmpv6InIpv4 => None,
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{TransportChecksumError::*, *};
+    use crate::err::ValueType;
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        assert_eq!("Icmpv6InIpv4", format!("{:?}", Icmpv6InIpv4));
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Icmpv6InIpv4;
+        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() {
+        // PayloadLen
+        {
+            let err = ValueTooBigError {
+                actual: 1,
+                max_allowed: 2,
+                value_type: ValueType::TcpPayloadLengthIpv6,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", PayloadLen(err)));
+        }
+
+        // Icmpv6InIpv4
+        assert_eq!(
+            format!("{}", Icmpv6InIpv4),
+            "Error: ICMPv6 can not be combined with an IPv4 headers (checksum can not be calculated)."
+        );
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        // Len
+        {
+            let err = ValueTooBigError {
+                actual: 1,
+                max_allowed: 2,
+                value_type: ValueType::TcpPayloadLengthIpv6,
+            };
+            assert!(PayloadLen(err).source().is_some());
+        }
+
+        // IpHeader
+        assert!(Icmpv6InIpv4.source().is_none());
+    }
+}
diff --git a/src/err/read_error.rs b/src/err/read_error.rs
new file mode 100644
index 0000000..8fab928
--- /dev/null
+++ b/src/err/read_error.rs
@@ -0,0 +1,1138 @@
+use crate::err::*;
+
+/// "Catch all" error for all `from_slice` or `read` errors (supports automatic conversion from all
+/// other slice errors).
+///
+/// This type aggregates all errors that can be caused by decoding from a slice or reading
+/// from an io stream.
+///
+/// This type can be used as a "catch all" type for errors caused by `from_slice` or
+/// `read` functions as all errors from these functions can be converted into this type.
+#[derive(Debug)]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub enum ReadError {
+    /// IO error was encountered while reading header or expected packet contents.
+    Io(std::io::Error),
+
+    /// Error when parsing had to be aborted because of a length error (usually
+    /// not enough data being available).
+    Len(LenError),
+
+    /// Error while parsing a double vlan header.
+    DoubleVlan(double_vlan::HeaderError),
+
+    /// Error while parsing a IP header.
+    Ip(ip::HeaderError),
+
+    /// Error while parsing a IP authentication header.
+    IpAuth(ip_auth::HeaderError),
+
+    /// Error while parsing a IPv4 header.
+    Ipv4(ipv4::HeaderError),
+
+    /// Error while parsing a IPv6 header.
+    Ipv6(ipv6::HeaderError),
+
+    /// Error while parsing a IPv6 extension header.
+    Ipv6Exts(ipv6_exts::HeaderError),
+
+    /// Error while parsing a Linux Cooked Capture v1 (SLL)
+    LinuxSll(linux_sll::HeaderError),
+
+    /// Error while parsing a TCP extension header.
+    Tcp(tcp::HeaderError),
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl ReadError {
+    pub fn io(&self) -> Option<&std::io::Error> {
+        match self {
+            ReadError::Io(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn len(&self) -> Option<&LenError> {
+        match self {
+            ReadError::Len(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn double_vlan(&self) -> Option<&double_vlan::HeaderError> {
+        match self {
+            ReadError::DoubleVlan(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn ip(&self) -> Option<&ip::HeaderError> {
+        match self {
+            ReadError::Ip(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn ip_auth(&self) -> Option<&ip_auth::HeaderError> {
+        match self {
+            ReadError::IpAuth(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn ipv4(&self) -> Option<&ipv4::HeaderError> {
+        match self {
+            ReadError::Ipv4(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn ipv6(&self) -> Option<&ipv6::HeaderError> {
+        match self {
+            ReadError::Ipv6(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn ipv6_exts(&self) -> Option<&ipv6_exts::HeaderError> {
+        match self {
+            ReadError::Ipv6Exts(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn linux_sll(&self) -> Option<&linux_sll::HeaderError> {
+        match self {
+            ReadError::LinuxSll(err) => Some(err),
+            _ => None,
+        }
+    }
+    pub fn tcp(&self) -> Option<&tcp::HeaderError> {
+        match self {
+            ReadError::Tcp(err) => Some(err),
+            _ => None,
+        }
+    }
+}
+
+impl core::fmt::Display for ReadError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use crate::err::ReadError::*;
+        match self {
+            Io(err) => err.fmt(f),
+            Len(err) => err.fmt(f),
+            DoubleVlan(err) => err.fmt(f),
+            Ip(err) => err.fmt(f),
+            IpAuth(err) => err.fmt(f),
+            Ipv4(err) => err.fmt(f),
+            Ipv6(err) => err.fmt(f),
+            Ipv6Exts(err) => err.fmt(f),
+            LinuxSll(err) => err.fmt(f),
+            Tcp(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for ReadError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        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 & len error conversions
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<std::io::Error> for ReadError {
+    fn from(value: std::io::Error) -> Self {
+        ReadError::Io(value)
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<LenError> for ReadError {
+    fn from(value: LenError) -> Self {
+        ReadError::Len(value)
+    }
+}
+
+// double vlan error conversions
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<double_vlan::HeaderError> for ReadError {
+    fn from(value: double_vlan::HeaderError) -> Self {
+        ReadError::DoubleVlan(value)
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<double_vlan::HeaderReadError> 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<double_vlan::HeaderSliceError> 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<ip::HeaderError> for ReadError {
+    fn from(value: ip::HeaderError) -> Self {
+        ReadError::Ip(value)
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ip::HeadersError> for ReadError {
+    fn from(value: ip::HeadersError) -> Self {
+        match value {
+            ip::HeadersError::Ip(err) => ReadError::Ip(err),
+            ip::HeadersError::Ipv4Ext(err) => ReadError::IpAuth(err),
+            ip::HeadersError::Ipv6Ext(err) => ReadError::Ipv6Exts(err),
+        }
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ip::HeaderReadError> for ReadError {
+    fn from(value: ip::HeaderReadError) -> Self {
+        use ip::HeaderReadError::*;
+        match value {
+            Io(err) => ReadError::Io(err),
+            Len(err) => ReadError::Len(err),
+            Content(err) => err.into(),
+        }
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ip::HeadersSliceError> for ReadError {
+    fn from(value: ip::HeadersSliceError) -> Self {
+        use ip::HeadersSliceError::*;
+        match value {
+            Len(err) => ReadError::Len(err),
+            Content(err) => err.into(),
+        }
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ip::SliceError> for ReadError {
+    fn from(value: ip::SliceError) -> Self {
+        use ip::SliceError::*;
+        match value {
+            Len(err) => ReadError::Len(err),
+            IpHeaders(err) => err.into(),
+        }
+    }
+}
+
+// ip auth error conversions
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ip_auth::HeaderError> for ReadError {
+    fn from(value: ip_auth::HeaderError) -> Self {
+        ReadError::IpAuth(value)
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ip_auth::HeaderReadError> for ReadError {
+    fn from(value: ip_auth::HeaderReadError) -> Self {
+        use ip_auth::HeaderReadError::*;
+        match value {
+            Io(err) => ReadError::Io(err),
+            Content(err) => ReadError::IpAuth(err),
+        }
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ip_auth::HeaderSliceError> for ReadError {
+    fn from(value: ip_auth::HeaderSliceError) -> Self {
+        use ip_auth::HeaderSliceError::*;
+        match value {
+            Len(err) => ReadError::Len(err),
+            Content(err) => ReadError::IpAuth(err),
+        }
+    }
+}
+
+// ipv4 error conversions
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ipv4::HeaderError> for ReadError {
+    fn from(value: ipv4::HeaderError) -> Self {
+        ReadError::Ipv4(value)
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ipv4::HeaderReadError> for ReadError {
+    fn from(value: ipv4::HeaderReadError) -> Self {
+        use ipv4::HeaderReadError::*;
+        match value {
+            Io(err) => ReadError::Io(err),
+            Content(err) => ReadError::Ipv4(err),
+        }
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ipv4::HeaderSliceError> for ReadError {
+    fn from(value: ipv4::HeaderSliceError) -> Self {
+        use ipv4::HeaderSliceError::*;
+        match value {
+            Len(err) => ReadError::Len(err),
+            Content(err) => ReadError::Ipv4(err),
+        }
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ipv4::SliceError> for ReadError {
+    fn from(value: ipv4::SliceError) -> Self {
+        use ipv4::SliceError::*;
+        match value {
+            Len(err) => ReadError::Len(err),
+            Header(err) => ReadError::Ipv4(err),
+            Exts(err) => ReadError::IpAuth(err),
+        }
+    }
+}
+
+// ipv6 error conversions
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ipv6::HeaderError> for ReadError {
+    fn from(value: ipv6::HeaderError) -> Self {
+        ReadError::Ipv6(value)
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ipv6::HeaderReadError> for ReadError {
+    fn from(value: ipv6::HeaderReadError) -> Self {
+        use ipv6::HeaderReadError::*;
+        match value {
+            Io(err) => ReadError::Io(err),
+            Content(err) => ReadError::Ipv6(err),
+        }
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ipv6::HeaderSliceError> for ReadError {
+    fn from(value: ipv6::HeaderSliceError) -> Self {
+        use ipv6::HeaderSliceError::*;
+        match value {
+            Len(err) => ReadError::Len(err),
+            Content(err) => ReadError::Ipv6(err),
+        }
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ipv6::SliceError> for ReadError {
+    fn from(value: ipv6::SliceError) -> Self {
+        use ipv6::SliceError::*;
+        match value {
+            Len(err) => ReadError::Len(err),
+            Header(err) => ReadError::Ipv6(err),
+            Exts(err) => ReadError::Ipv6Exts(err),
+        }
+    }
+}
+
+// ipv6 exts error conversions
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ipv6_exts::HeaderError> for ReadError {
+    fn from(value: ipv6_exts::HeaderError) -> Self {
+        ReadError::Ipv6Exts(value)
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ipv6_exts::HeaderReadError> for ReadError {
+    fn from(value: ipv6_exts::HeaderReadError) -> Self {
+        use ipv6_exts::HeaderReadError::*;
+        match value {
+            Io(err) => ReadError::Io(err),
+            Content(err) => ReadError::Ipv6Exts(err),
+        }
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<ipv6_exts::HeaderSliceError> for ReadError {
+    fn from(value: ipv6_exts::HeaderSliceError) -> Self {
+        use ipv6_exts::HeaderSliceError::*;
+        match value {
+            Len(err) => ReadError::Len(err),
+            Content(err) => ReadError::Ipv6Exts(err),
+        }
+    }
+}
+
+// linux sll error conversions
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<linux_sll::HeaderError> for ReadError {
+    fn from(value: linux_sll::HeaderError) -> Self {
+        ReadError::LinuxSll(value)
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<linux_sll::HeaderReadError> for ReadError {
+    fn from(value: linux_sll::HeaderReadError) -> Self {
+        use linux_sll::HeaderReadError::*;
+        match value {
+            Io(err) => ReadError::Io(err),
+            Content(err) => ReadError::LinuxSll(err),
+        }
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<linux_sll::HeaderSliceError> for ReadError {
+    fn from(value: linux_sll::HeaderSliceError) -> Self {
+        use linux_sll::HeaderSliceError::*;
+        match value {
+            Len(err) => ReadError::Len(err),
+            Content(err) => ReadError::LinuxSll(err),
+        }
+    }
+}
+
+// packet error conversions
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<packet::SliceError> for ReadError {
+    fn from(value: packet::SliceError) -> Self {
+        use packet::SliceError::*;
+        match value {
+            Len(err) => ReadError::Len(err),
+            LinuxSll(err) => ReadError::LinuxSll(err),
+            Ip(err) => ReadError::Ip(err),
+            Ipv4(err) => ReadError::Ipv4(err),
+            Ipv6(err) => ReadError::Ipv6(err),
+            Ipv4Exts(err) => ReadError::IpAuth(err),
+            Ipv6Exts(err) => ReadError::Ipv6Exts(err),
+            Tcp(err) => ReadError::Tcp(err),
+        }
+    }
+}
+
+// tcp error conversions
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<tcp::HeaderError> for ReadError {
+    fn from(value: tcp::HeaderError) -> Self {
+        ReadError::Tcp(value)
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<tcp::HeaderReadError> for ReadError {
+    fn from(value: tcp::HeaderReadError) -> Self {
+        use tcp::HeaderReadError::*;
+        match value {
+            Io(err) => ReadError::Io(err),
+            Content(err) => ReadError::Tcp(err),
+        }
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl From<tcp::HeaderSliceError> for ReadError {
+    fn from(value: tcp::HeaderSliceError) -> Self {
+        use tcp::HeaderSliceError::*;
+        match value {
+            Len(err) => ReadError::Len(err),
+            Content(err) => ReadError::Tcp(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::{
+        err::{ReadError::*, *},
+        LenSource,
+    };
+    use crate::{ArpHardwareId, EtherType};
+    use std::error::Error;
+    use std::format;
+
+    #[test]
+    fn debug_source() {
+        let test_values: [(&str, ReadError); 10] = [
+            (
+                "Len",
+                Len(LenError {
+                    required_len: 0,
+                    len: 0,
+                    len_source: LenSource::Slice,
+                    layer: Layer::Icmpv4,
+                    layer_start_offset: 0,
+                }),
+            ),
+            (
+                "LinuxSll",
+                LinuxSll(linux_sll::HeaderError::UnsupportedArpHardwareId {
+                    arp_hardware_type: ArpHardwareId::ETHER,
+                }),
+            ),
+            (
+                "DoubleVlan",
+                DoubleVlan(double_vlan::HeaderError::NonVlanEtherType {
+                    unexpected_ether_type: EtherType(123),
+                }),
+            ),
+            (
+                "Ip",
+                Ip(ip::HeaderError::UnsupportedIpVersion {
+                    version_number: 123,
+                }),
+            ),
+            ("IpAuth", IpAuth(ip_auth::HeaderError::ZeroPayloadLen)),
+            (
+                "Ipv4",
+                Ipv4(ipv4::HeaderError::UnexpectedVersion { version_number: 1 }),
+            ),
+            (
+                "Ipv6",
+                Ipv6(ipv6::HeaderError::UnexpectedVersion { version_number: 1 }),
+            ),
+            (
+                "Ipv6Exts",
+                Ipv6Exts(ipv6_exts::HeaderError::HopByHopNotAtStart),
+            ),
+            (
+                "LinuxSll",
+                LinuxSll(linux_sll::HeaderError::UnsupportedPacketTypeField { packet_type: 123 }),
+            ),
+            (
+                "Tcp",
+                Tcp(tcp::HeaderError::DataOffsetTooSmall { data_offset: 1 }),
+            ),
+        ];
+        for (prefix, value) in &test_values {
+            // display
+            assert_eq!(
+                format!("{:?}", value),
+                format!("{}({:?})", prefix, value.source().unwrap())
+            );
+        }
+        // io handled separately as source points to the underlying type
+        {
+            let io_error = || std::io::Error::new(std::io::ErrorKind::Other, "some error");
+            assert_eq!(
+                format!("Io({:?})", io_error()),
+                format!("{:?}", Io(io_error()))
+            );
+        }
+    }
+
+    #[test]
+    fn display_source() {
+        let test_values: [ReadError; 10] = [
+            Len(LenError {
+                required_len: 0,
+                len: 0,
+                len_source: LenSource::Slice,
+                layer: Layer::Icmpv4,
+                layer_start_offset: 0,
+            }),
+            LinuxSll(linux_sll::HeaderError::UnsupportedArpHardwareId {
+                arp_hardware_type: ArpHardwareId::ETHER,
+            }),
+            DoubleVlan(double_vlan::HeaderError::NonVlanEtherType {
+                unexpected_ether_type: EtherType(123),
+            }),
+            Ip(ip::HeaderError::UnsupportedIpVersion {
+                version_number: 123,
+            }),
+            IpAuth(ip_auth::HeaderError::ZeroPayloadLen),
+            Ipv4(ipv4::HeaderError::UnexpectedVersion { version_number: 1 }),
+            Ipv6(ipv6::HeaderError::UnexpectedVersion { version_number: 1 }),
+            Ipv6Exts(ipv6_exts::HeaderError::HopByHopNotAtStart),
+            LinuxSll(linux_sll::HeaderError::UnsupportedPacketTypeField { packet_type: 123 }),
+            Tcp(tcp::HeaderError::DataOffsetTooSmall { data_offset: 1 }),
+        ];
+        for value in &test_values {
+            // display
+            assert_eq!(format!("{}", value), format!("{}", value.source().unwrap()));
+        }
+        // io handled separately as source points to the underlying type
+        {
+            let io_error = || std::io::Error::new(std::io::ErrorKind::Other, "some error");
+            assert_eq!(format!("{}", io_error()), format!("{}", Io(io_error())));
+            assert!(Io(io_error()).source().is_some());
+        }
+    }
+
+    #[test]
+    fn accessors() {
+        use ReadError::*;
+        let io_error = || std::io::Error::new(std::io::ErrorKind::Other, "some error");
+        let len_error = || LenError {
+            required_len: 0,
+            len: 0,
+            len_source: LenSource::Slice,
+            layer: Layer::Icmpv4,
+            layer_start_offset: 0,
+        };
+        let double_vlan_error = || double_vlan::HeaderError::NonVlanEtherType {
+            unexpected_ether_type: EtherType(1),
+        };
+        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 };
+        let ip_auth_error = || ip_auth::HeaderError::ZeroPayloadLen;
+        let ipv6_exts_error = || ipv6_exts::HeaderError::HopByHopNotAtStart;
+        let linux_sll_error =
+            || linux_sll::HeaderError::UnsupportedPacketTypeField { packet_type: 123 };
+        let tcp_error = || tcp::HeaderError::DataOffsetTooSmall { data_offset: 1 };
+
+        // io
+        assert!(Io(io_error()).io().is_some());
+        assert!(Ipv4(ipv4_error()).io().is_none());
+
+        // len
+        assert_eq!(Len(len_error()).len(), Some(&len_error()));
+        assert_eq!(Ipv4(ipv4_error()).len(), None);
+
+        // linux sll
+        assert_eq!(
+            LinuxSll(linux_sll_error()).linux_sll(),
+            Some(&linux_sll_error())
+        );
+        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);
+
+        // ip
+        assert_eq!(Ip(ip_error()).ip(), Some(&ip_error()));
+        assert_eq!(Ipv4(ipv4_error()).ip(), None);
+
+        // ip_auth
+        assert_eq!(IpAuth(ip_auth_error()).ip_auth(), Some(&ip_auth_error()));
+        assert_eq!(Ipv4(ipv4_error()).ip_auth(), None);
+
+        // ipv4
+        assert_eq!(Ipv4(ipv4_error()).ipv4(), Some(&ipv4_error()));
+        assert_eq!(IpAuth(ip_auth_error()).ipv4(), None);
+
+        // ipv6
+        assert_eq!(Ipv6(ipv6_error()).ipv6(), Some(&ipv6_error()));
+        assert_eq!(IpAuth(ip_auth_error()).ipv6(), None);
+
+        // ipv6_exts
+        assert_eq!(
+            Ipv6Exts(ipv6_exts_error()).ipv6_exts(),
+            Some(&ipv6_exts_error())
+        );
+        assert_eq!(IpAuth(ip_auth_error()).ipv6_exts(), None);
+
+        // linux_sll
+        assert_eq!(
+            LinuxSll(linux_sll_error()).linux_sll(),
+            Some(&linux_sll_error())
+        );
+        assert_eq!(IpAuth(ip_auth_error()).linux_sll(), None);
+
+        // tcp
+        assert_eq!(Tcp(tcp_error()).tcp(), Some(&tcp_error()));
+        assert_eq!(IpAuth(ip_auth_error()).tcp(), None);
+    }
+
+    #[test]
+    fn from() {
+        let io_error =
+            || -> std::io::Error { std::io::Error::new(std::io::ErrorKind::Other, "some error") };
+        let len_error = || -> LenError {
+            LenError {
+                required_len: 0,
+                len: 0,
+                len_source: LenSource::Slice,
+                layer: Layer::Icmpv4,
+                layer_start_offset: 0,
+            }
+        };
+
+        // io & len
+        assert!(ReadError::from(io_error()).io().is_some());
+        assert_eq!(&len_error(), ReadError::from(len_error()).len().unwrap());
+
+        // linux sll
+        {
+            let header_error = || linux_sll::HeaderError::UnsupportedArpHardwareId {
+                arp_hardware_type: ArpHardwareId::ETHER,
+            };
+            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()
+            );
+        }
+
+        // double vlan errors
+        {
+            let header_error = || double_vlan::HeaderError::NonVlanEtherType {
+                unexpected_ether_type: EtherType(123),
+            };
+            assert_eq!(
+                &header_error(),
+                ReadError::from(header_error()).double_vlan().unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(double_vlan::HeaderReadError::Content(header_error()))
+                    .double_vlan()
+                    .unwrap()
+            );
+            assert!(
+                ReadError::from(double_vlan::HeaderReadError::Io(io_error()))
+                    .io()
+                    .is_some()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(double_vlan::HeaderSliceError::Content(header_error()))
+                    .double_vlan()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                ReadError::from(double_vlan::HeaderSliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(double_vlan::HeaderSliceError::Content(header_error()))
+                    .double_vlan()
+                    .unwrap()
+            );
+        }
+
+        // ip errors
+        {
+            let header_error = || ip::HeaderError::UnsupportedIpVersion {
+                version_number: 123,
+            };
+            assert_eq!(
+                &header_error(),
+                ReadError::from(header_error()).ip().unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ip::HeaderReadError::Content(ip::HeadersError::Ip(
+                    header_error()
+                )))
+                .ip()
+                .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                ReadError::from(ip::HeaderReadError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert!(ReadError::from(ip::HeaderReadError::Io(io_error()))
+                .io()
+                .is_some());
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ip::HeadersSliceError::Content(ip::HeadersError::Ip(
+                    header_error()
+                )))
+                .ip()
+                .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                ReadError::from(ip::HeadersSliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ip::HeadersSliceError::Content(ip::HeadersError::Ip(
+                    header_error()
+                )))
+                .ip()
+                .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                ReadError::from(ip::SliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ip::SliceError::IpHeaders(ip::HeadersError::Ip(
+                    header_error()
+                )))
+                .ip()
+                .unwrap()
+            );
+        }
+
+        // ip auth errors
+        {
+            let header_error = || ip_auth::HeaderError::ZeroPayloadLen;
+            assert_eq!(
+                &header_error(),
+                ReadError::from(header_error()).ip_auth().unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ip_auth::HeaderReadError::Content(header_error()))
+                    .ip_auth()
+                    .unwrap()
+            );
+            assert!(ReadError::from(ip_auth::HeaderReadError::Io(io_error()))
+                .io()
+                .is_some());
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ip_auth::HeaderSliceError::Content(header_error()))
+                    .ip_auth()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                ReadError::from(ip_auth::HeaderSliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ip_auth::HeaderSliceError::Content(header_error()))
+                    .ip_auth()
+                    .unwrap()
+            );
+        }
+
+        // ipv4 errors
+        {
+            let header_error = || ipv4::HeaderError::UnexpectedVersion {
+                version_number: 123,
+            };
+            let exts_error = || ip_auth::HeaderError::ZeroPayloadLen;
+            assert_eq!(
+                &header_error(),
+                ReadError::from(header_error()).ipv4().unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ipv4::HeaderReadError::Content(header_error()))
+                    .ipv4()
+                    .unwrap()
+            );
+            assert!(ReadError::from(ipv4::HeaderReadError::Io(io_error()))
+                .io()
+                .is_some());
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ipv4::HeaderSliceError::Content(header_error()))
+                    .ipv4()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                ReadError::from(ipv4::HeaderSliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ipv4::HeaderSliceError::Content(header_error()))
+                    .ipv4()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                ReadError::from(ipv4::SliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ipv4::SliceError::Header(header_error()))
+                    .ipv4()
+                    .unwrap()
+            );
+            assert_eq!(
+                &exts_error(),
+                ReadError::from(ipv4::SliceError::Exts(exts_error()))
+                    .ip_auth()
+                    .unwrap()
+            );
+        }
+
+        // ipv6 errors
+        {
+            let header_error = || ipv6::HeaderError::UnexpectedVersion {
+                version_number: 123,
+            };
+            let exts_error = || ipv6_exts::HeaderError::HopByHopNotAtStart;
+            assert_eq!(
+                &header_error(),
+                ReadError::from(header_error()).ipv6().unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ipv6::HeaderReadError::Content(header_error()))
+                    .ipv6()
+                    .unwrap()
+            );
+            assert!(ReadError::from(ipv6::HeaderReadError::Io(io_error()))
+                .io()
+                .is_some());
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ipv6::HeaderSliceError::Content(header_error()))
+                    .ipv6()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                ReadError::from(ipv6::HeaderSliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ipv6::HeaderSliceError::Content(header_error()))
+                    .ipv6()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                ReadError::from(ipv6::SliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ipv6::SliceError::Header(header_error()))
+                    .ipv6()
+                    .unwrap()
+            );
+            assert_eq!(
+                &exts_error(),
+                ReadError::from(ipv6::SliceError::Exts(exts_error()))
+                    .ipv6_exts()
+                    .unwrap()
+            );
+        }
+
+        // ipv6 exts errors
+        {
+            let header_error = || ipv6_exts::HeaderError::HopByHopNotAtStart;
+            assert_eq!(
+                &header_error(),
+                ReadError::from(header_error()).ipv6_exts().unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ipv6_exts::HeaderReadError::Content(header_error()))
+                    .ipv6_exts()
+                    .unwrap()
+            );
+            assert!(ReadError::from(ipv6_exts::HeaderReadError::Io(io_error()))
+                .io()
+                .is_some());
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ipv6_exts::HeaderSliceError::Content(header_error()))
+                    .ipv6_exts()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                ReadError::from(ipv6_exts::HeaderSliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(ipv6_exts::HeaderSliceError::Content(header_error()))
+                    .ipv6_exts()
+                    .unwrap()
+            );
+        }
+
+        // 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 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 };
+            let ip_auth_error = || ip_auth::HeaderError::ZeroPayloadLen;
+            let ipv6_exts_error = || ipv6_exts::HeaderError::HopByHopNotAtStart;
+            let tcp_error = || tcp::HeaderError::DataOffsetTooSmall { data_offset: 1 };
+
+            // IpSliceError
+            assert_eq!(
+                &len_error(),
+                ReadError::from(packet::SliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &ip_error(),
+                ReadError::from(packet::SliceError::Ip(ip_error()))
+                    .ip()
+                    .unwrap()
+            );
+            assert_eq!(
+                &ipv4_error(),
+                ReadError::from(packet::SliceError::Ipv4(ipv4_error()))
+                    .ipv4()
+                    .unwrap()
+            );
+            assert_eq!(
+                &ipv6_error(),
+                ReadError::from(packet::SliceError::Ipv6(ipv6_error()))
+                    .ipv6()
+                    .unwrap()
+            );
+            assert_eq!(
+                &ip_auth_error(),
+                ReadError::from(packet::SliceError::Ipv4Exts(ip_auth_error()))
+                    .ip_auth()
+                    .unwrap()
+            );
+            assert_eq!(
+                &ipv6_exts_error(),
+                ReadError::from(packet::SliceError::Ipv6Exts(ipv6_exts_error()))
+                    .ipv6_exts()
+                    .unwrap()
+            );
+            assert_eq!(
+                &tcp_error(),
+                ReadError::from(packet::SliceError::Tcp(tcp_error()))
+                    .tcp()
+                    .unwrap()
+            );
+        }
+
+        // tcp errors
+        {
+            let header_error = || tcp::HeaderError::DataOffsetTooSmall { data_offset: 1 };
+            assert_eq!(
+                &header_error(),
+                ReadError::from(header_error()).tcp().unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(tcp::HeaderReadError::Content(header_error()))
+                    .tcp()
+                    .unwrap()
+            );
+            assert!(ReadError::from(tcp::HeaderReadError::Io(io_error()))
+                .io()
+                .is_some());
+            assert_eq!(
+                &header_error(),
+                ReadError::from(tcp::HeaderSliceError::Content(header_error()))
+                    .tcp()
+                    .unwrap()
+            );
+            assert_eq!(
+                &len_error(),
+                ReadError::from(tcp::HeaderSliceError::Len(len_error()))
+                    .len()
+                    .unwrap()
+            );
+            assert_eq!(
+                &header_error(),
+                ReadError::from(tcp::HeaderSliceError::Content(header_error()))
+                    .tcp()
+                    .unwrap()
+            );
+        }
+    }
+} // mod tests
diff --git a/src/err/slice_write_space_error.rs b/src/err/slice_write_space_error.rs
new file mode 100644
index 0000000..15bed00
--- /dev/null
+++ b/src/err/slice_write_space_error.rs
@@ -0,0 +1,148 @@
+use crate::err::Layer;
+
+/// Error when not enough space is available in a slice
+/// to write a packet or header to it.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub struct SliceWriteSpaceError {
+    /// Expected minimum length conflicting with the
+    /// `actual_len` value.
+    pub required_len: usize,
+
+    /// Length limiting or exceeding the required length.
+    pub len: usize,
+
+    /// Layer in which could not be written to the slice.
+    pub layer: Layer,
+
+    /// Offset from the start of the parsed data to the layer where the
+    /// length error occurred.
+    pub layer_start_offset: usize,
+}
+
+impl core::fmt::Display for SliceWriteSpaceError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        if self.layer_start_offset > 0 {
+            write!(
+                f,
+                "Not enough space to write {} to slice. Needed {} byte(s), but only {} byte(s) were available (start offset of {} write was {} byte(s)).",
+                self.layer,
+                self.required_len,
+                self.len,
+                self.layer,
+                self.layer_start_offset
+            )
+        } else {
+            write!(
+                f,
+                "Not enough space to write {} to slice. Needed {} byte(s), but only {} byte(s) were available.",
+                self.layer,
+                self.required_len,
+                self.len,
+            )
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for SliceWriteSpaceError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        None
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        assert_eq!(
+            format!(
+                "{:?}",
+                SliceWriteSpaceError {
+                    required_len: 2,
+                    layer: Layer::Ipv4Header,
+                    len: 1,
+                    layer_start_offset: 0
+                }
+            ),
+            format!(
+                "SliceWriteSpaceError {{ required_len: {:?}, len: {:?}, layer: {:?}, layer_start_offset: {:?} }}",
+                2, 1, Layer::Ipv4Header, 0
+            ),
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = SliceWriteSpaceError {
+            required_len: 2,
+            layer: Layer::Icmpv4,
+            len: 1,
+            layer_start_offset: 20,
+        };
+        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() {
+        // layer_start_offset set
+        assert_eq!(
+            "Not enough space to write IPv4 header to slice. Needed 2 byte(s), but only 1 byte(s) were available (start offset of IPv4 header write was 4 byte(s)).",
+            format!(
+                "{}",
+                SliceWriteSpaceError{
+                    required_len: 2,
+                    len: 1,
+                    layer: Layer::Ipv4Header,
+                    layer_start_offset: 4
+                }
+            )
+        );
+
+        // layer_start_offset zero
+        assert_eq!(
+            "Not enough space to write IPv4 header to slice. Needed 4 byte(s), but only 3 byte(s) were available.",
+            format!(
+                "{}",
+                SliceWriteSpaceError{
+                    required_len: 4,
+                    len: 3,
+                    layer: Layer::Ipv4Header,
+                    layer_start_offset: 0
+                }
+            )
+        );
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(SliceWriteSpaceError {
+            required_len: 0,
+            len: 0,
+            layer: Layer::Ipv4Header,
+            layer_start_offset: 0
+        }
+        .source()
+        .is_none());
+    }
+}
diff --git a/src/err/tcp/header_error.rs b/src/err/tcp/header_error.rs
new file mode 100644
index 0000000..788663d
--- /dev/null
+++ b/src/err/tcp/header_error.rs
@@ -0,0 +1,78 @@
+/// Errors that can be encountered while decoding a TCP header.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeaderError {
+    /// Error when the data_offset is so small that the data would
+    /// start within the TCP header itself.
+    DataOffsetTooSmall { data_offset: u8 },
+}
+
+impl core::fmt::Display for HeaderError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderError::*;
+        match self {
+            DataOffsetTooSmall{ data_offset } => write!(
+                f,
+                "TCP Header Error: 'data offset' too small ({}). The 'data offset' must be at least 5 so the data is not overlapping with the TCP header itself.",
+                data_offset
+            ),
+        }
+    }
+}
+
+#[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!(
+            "DataOffsetTooSmall { data_offset: 1 }",
+            format!("{:?}", DataOffsetTooSmall { data_offset: 1 })
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = DataOffsetTooSmall { data_offset: 1 };
+        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!(
+            "TCP Header Error: 'data offset' too small (1). The 'data offset' must be at least 5 so the data is not overlapping with the TCP header itself.",
+            format!("{}", DataOffsetTooSmall{ data_offset: 1 })
+        );
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(DataOffsetTooSmall { data_offset: 0 }.source().is_none());
+    }
+}
diff --git a/src/err/tcp/header_read_error.rs b/src/err/tcp/header_read_error.rs
new file mode 100644
index 0000000..b4611c6
--- /dev/null
+++ b/src/err/tcp/header_read_error.rs
@@ -0,0 +1,139 @@
+use super::HeaderError;
+
+/// Error when decoding a TCP header via a `std::io::Read` source.
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+#[derive(Debug)]
+pub enum HeaderReadError {
+    /// IO error was encountered while reading header.
+    Io(std::io::Error),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl HeaderReadError {
+    /// Returns the `std::io::Error` value if the `HeaderReadError` is `Io`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn io_error(self) -> Option<std::io::Error> {
+        use HeaderReadError::*;
+        match self {
+            Io(value) => Some(value),
+            _ => None,
+        }
+    }
+
+    /// Returns the `err::tcp::HeaderError` value if it is of value `Content`.
+    /// Otherwise `None` is returned.
+    #[inline]
+    pub fn content_error(self) -> Option<HeaderError> {
+        use HeaderReadError::*;
+        match self {
+            Content(value) => Some(value),
+            _ => None,
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+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, "TCP Header IO Error: {}", err),
+            Content(value) => value.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderReadError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        use HeaderReadError::*;
+        match self {
+            Io(err) => Some(err),
+            Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(all(test, feature = "std"))]
+mod test {
+    use super::{HeaderReadError::*, *};
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::DataOffsetTooSmall { data_offset: 1 };
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn fmt() {
+        {
+            let err = std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            );
+            assert_eq!(
+                format!("TCP Header IO Error: {}", err),
+                format!("{}", Io(err))
+            );
+        }
+        {
+            let err = HeaderError::DataOffsetTooSmall { data_offset: 1 };
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[test]
+    fn source() {
+        use std::error::Error;
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .source()
+        .is_some());
+        assert!(Content(HeaderError::DataOffsetTooSmall { data_offset: 1 })
+            .source()
+            .is_some());
+    }
+
+    #[test]
+    fn io_error() {
+        assert!(Io(std::io::Error::new(
+            std::io::ErrorKind::UnexpectedEof,
+            "failed to fill whole buffer",
+        ))
+        .io_error()
+        .is_some());
+        assert!(Content(HeaderError::DataOffsetTooSmall { data_offset: 1 })
+            .io_error()
+            .is_none());
+    }
+
+    #[test]
+    fn content_error() {
+        assert_eq!(
+            None,
+            Io(std::io::Error::new(
+                std::io::ErrorKind::UnexpectedEof,
+                "failed to fill whole buffer",
+            ))
+            .content_error()
+        );
+        {
+            let err = HeaderError::DataOffsetTooSmall { data_offset: 1 };
+            assert_eq!(Some(err.clone()), Content(err.clone()).content_error());
+        }
+    }
+}
diff --git a/src/err/tcp/header_slice_error.rs b/src/err/tcp/header_slice_error.rs
new file mode 100644
index 0000000..c6f0859
--- /dev/null
+++ b/src/err/tcp/header_slice_error.rs
@@ -0,0 +1,145 @@
+use super::HeaderError;
+use crate::err::LenError;
+
+/// Error when decoding a TCP header from a slice.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum HeaderSliceError {
+    /// Error when an length error is encountered (e.g. unexpected
+    /// end of slice).
+    Len(LenError),
+
+    /// Error caused by the contents of the header.
+    Content(HeaderError),
+}
+
+impl HeaderSliceError {
+    /// Adds an offset value to all slice length related fields.
+    #[inline]
+    pub const fn add_slice_offset(self, offset: usize) -> Self {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => Len(err.add_offset(offset)),
+            Content(err) => Content(err),
+        }
+    }
+}
+
+impl core::fmt::Display for HeaderSliceError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use HeaderSliceError::*;
+        match self {
+            Len(err) => err.fmt(f),
+            Content(err) => err.fmt(f),
+        }
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for HeaderSliceError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        match self {
+            HeaderSliceError::Len(err) => Some(err),
+            HeaderSliceError::Content(err) => Some(err),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{HeaderSliceError::*, *};
+    use crate::{err::Layer, LenSource};
+    use alloc::format;
+    use std::{
+        collections::hash_map::DefaultHasher,
+        error::Error,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn add_slice_offset() {
+        use HeaderSliceError::*;
+        assert_eq!(
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3
+            })
+            .add_slice_offset(200),
+            Len(LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 203
+            })
+        );
+        assert_eq!(
+            Content(HeaderError::DataOffsetTooSmall { data_offset: 1 }).add_slice_offset(200),
+            Content(HeaderError::DataOffsetTooSmall { data_offset: 1 })
+        );
+    }
+
+    #[test]
+    fn debug() {
+        let err = HeaderError::DataOffsetTooSmall { data_offset: 1 };
+        assert_eq!(
+            format!("Content({:?})", err.clone()),
+            format!("{:?}", Content(err))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = Content(HeaderError::DataOffsetTooSmall { data_offset: 1 });
+        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() {
+        {
+            let err = LenError {
+                required_len: 1,
+                layer: Layer::Icmpv4,
+                len: 2,
+                len_source: LenSource::Slice,
+                layer_start_offset: 3,
+            };
+            assert_eq!(format!("{}", &err), format!("{}", Len(err)));
+        }
+        {
+            let err = HeaderError::DataOffsetTooSmall { data_offset: 1 };
+            assert_eq!(format!("{}", &err), format!("{}", Content(err.clone())));
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(Len(LenError {
+            required_len: 1,
+            layer: Layer::Icmpv4,
+            len: 2,
+            len_source: LenSource::Slice,
+            layer_start_offset: 3
+        })
+        .source()
+        .is_some());
+        assert!(Content(HeaderError::DataOffsetTooSmall { data_offset: 1 })
+            .source()
+            .is_some());
+    }
+}
diff --git a/src/err/tcp/mod.rs b/src/err/tcp/mod.rs
new file mode 100644
index 0000000..bf4f0e8
--- /dev/null
+++ b/src/err/tcp/mod.rs
@@ -0,0 +1,10 @@
+mod header_error;
+pub use header_error::*;
+
+#[cfg(feature = "std")]
+mod header_read_error;
+#[cfg(feature = "std")]
+pub use header_read_error::*;
+
+mod header_slice_error;
+pub use header_slice_error::*;
diff --git a/src/err/value_too_big_error.rs b/src/err/value_too_big_error.rs
new file mode 100644
index 0000000..021c7d5
--- /dev/null
+++ b/src/err/value_too_big_error.rs
@@ -0,0 +1,113 @@
+use crate::err;
+use core::{cmp::Eq, cmp::PartialEq, fmt::Debug, fmt::Display, hash::Hash};
+
+/// Error if a value exceeds the maximum allowed value.
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub struct ValueTooBigError<T: Sized + Clone + Display + Debug + Eq + PartialEq + Hash> {
+    /// Value that was disallowed.
+    pub actual: T,
+
+    /// Maximum allowed value (inclusive).
+    pub max_allowed: T,
+
+    /// Type of value.
+    pub value_type: err::ValueType,
+}
+
+impl<T> core::fmt::Display for ValueTooBigError<T>
+where
+    T: Sized + Clone + Display + Debug + Eq + PartialEq + Hash,
+{
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        write!(
+            f,
+            "Error '{}' is too big to be a '{}' (maximum allowed value is '{}')",
+            self.actual, self.value_type, self.max_allowed
+        )
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl<T> std::error::Error for ValueTooBigError<T>
+where
+    T: Sized + Clone + Display + Debug + Eq + PartialEq + Hash,
+{
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        None
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use std::{collections::hash_map::DefaultHasher, error::Error, format, hash::Hasher};
+
+    #[test]
+    fn fmt() {
+        assert_eq!(
+            format!(
+                "{}",
+                ValueTooBigError {
+                    actual: 3,
+                    max_allowed: 2,
+                    value_type: err::ValueType::IpFragmentOffset
+                }
+            ),
+            "Error '3' is too big to be a 'IP Fragment Offset' (maximum allowed value is '2')"
+        );
+    }
+
+    #[test]
+    fn dbg() {
+        assert_eq!(
+            format!(
+                "{:?}",
+                ValueTooBigError {
+                    actual: 3,
+                    max_allowed: 2,
+                    value_type: err::ValueType::IpFragmentOffset
+                }
+            ),
+            format!(
+                "ValueTooBigError {{ actual: {}, max_allowed: {}, value_type: {:?} }}",
+                3,
+                2,
+                err::ValueType::IpFragmentOffset
+            )
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash() {
+        let err = ValueTooBigError {
+            actual: 3,
+            max_allowed: 2,
+            value_type: err::ValueType::IpFragmentOffset,
+        };
+        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);
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source() {
+        assert!(ValueTooBigError {
+            actual: 3,
+            max_allowed: 2,
+            value_type: err::ValueType::IpFragmentOffset
+        }
+        .source()
+        .is_none());
+    }
+}
diff --git a/src/err/value_type.rs b/src/err/value_type.rs
new file mode 100644
index 0000000..b5d8405
--- /dev/null
+++ b/src/err/value_type.rs
@@ -0,0 +1,130 @@
+/// Types of values that have a limited allowed value range
+/// and can cause an [`crate::err::ValueTooBigError`].
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub enum ValueType {
+    /// VLAN identifier field present in a [`crate::SingleVlanHeader`].
+    VlanId,
+    /// VLAN PCP (Priority Code Point) field in a [`crate::SingleVlanHeader`].
+    VlanPcp,
+    /// IP Fragment offset present in the IPv4 header and
+    /// IPv6 fragmentation header.
+    IpFragmentOffset,
+    /// IPv4 Header DSCP (Differentiated Services Code Point) field
+    /// present in an [`crate::Ipv4Header`].
+    Ipv4Dscp,
+    /// IPv4 Header ECN (Explicit Congestion Notification) field
+    /// present in an [`crate::Ipv4Header`].
+    Ipv4Ecn,
+    /// IPv6 Header Flow Label field present in [`crate::Ipv6Header`].
+    Ipv6FlowLabel,
+    /// IPv4 Header "total length" field based on the payload
+    /// length after the header.
+    Ipv4PayloadLength,
+    /// IPv6 Header "payload length" field present in an
+    /// [`crate::Ipv6Header`].
+    Ipv6PayloadLength,
+    /// Payload length used when calculating the checksum of a
+    /// [`crate::UdpHeader`] for IPv4.
+    UdpPayloadLengthIpv4,
+    /// Payload length used when calculating the checksum of a
+    /// [`crate::UdpHeader`] for IPv6.
+    UdpPayloadLengthIpv6,
+    /// Payload length used when calculating the checksum of a
+    /// [`crate::TcpHeader`] for IPv4.
+    TcpPayloadLengthIpv4,
+    /// Payload length used when calculating the checksum of a
+    /// [`crate::TcpHeader`] for IPv6.
+    TcpPayloadLengthIpv6,
+    /// Variable length data of an ICMPv6 packet.
+    Icmpv6PayloadLength,
+    /// Packet type of a Linux Cooked Capture v1 (SLL)
+    LinuxSllType,
+}
+
+impl core::fmt::Display for ValueType {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use ValueType::*;
+        match self {
+            VlanId => write!(f, "VLAN ID"),
+            VlanPcp => write!(f, "VLAN PCP (Priority Code Point)"),
+            IpFragmentOffset => write!(f, "IP Fragment Offset"),
+            Ipv4Dscp => write!(f, "IPv4 DSCP (Differentiated Services Code Point)"),
+            Ipv4Ecn => write!(f, "IPv4 ECN (Explicit Congestion Notification)"),
+            Ipv6FlowLabel => write!(f, "IPv6 Flow Label"),
+            Ipv4PayloadLength => write!(f, "IPv4 Header 'Payload Length' (sets 'Total Length')"),
+            Ipv6PayloadLength => write!(f, "IPv6 Header 'Payload Length'"),
+            UdpPayloadLengthIpv4 => write!(f, "UDP Payload Length (in IPv4 checksum calculation)"),
+            UdpPayloadLengthIpv6 => write!(f, "UDP Payload Length (in IPv6 checksum calculation)"),
+            TcpPayloadLengthIpv4 => write!(f, "TCP Payload Length (in IPv4 checksum calculation)"),
+            TcpPayloadLengthIpv6 => write!(f, "TCP Payload Length (in IPv6 checksum calculation)"),
+            Icmpv6PayloadLength => write!(f, "ICMPv6 Payload Length"),
+            LinuxSllType => write!(f, "Linux Cooked Capture v1 (SLL)"),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use std::format;
+
+    #[test]
+    fn debug() {
+        assert_eq!(
+            format!("{:?}", ValueType::IpFragmentOffset),
+            "IpFragmentOffset"
+        );
+    }
+
+    #[test]
+    fn cmp_partial_cmp() {
+        use core::cmp::Ordering;
+        let a = ValueType::IpFragmentOffset;
+        let b = a;
+        assert_eq!(a.cmp(&b), Ordering::Equal);
+        assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));
+    }
+
+    #[test]
+    fn display() {
+        use ValueType::*;
+
+        assert_eq!("VLAN ID", &format!("{}", VlanId));
+        assert_eq!("VLAN PCP (Priority Code Point)", &format!("{}", VlanPcp));
+        assert_eq!("IP Fragment Offset", &format!("{}", IpFragmentOffset));
+        assert_eq!(
+            "IPv4 DSCP (Differentiated Services Code Point)",
+            &format!("{}", Ipv4Dscp)
+        );
+        assert_eq!(
+            "IPv4 ECN (Explicit Congestion Notification)",
+            &format!("{}", Ipv4Ecn)
+        );
+        assert_eq!("IPv6 Flow Label", &format!("{}", Ipv6FlowLabel));
+        assert_eq!(
+            "IPv4 Header 'Payload Length' (sets 'Total Length')",
+            &format!("{}", Ipv4PayloadLength)
+        );
+        assert_eq!(
+            "IPv6 Header 'Payload Length'",
+            &format!("{}", Ipv6PayloadLength)
+        );
+        assert_eq!(
+            "UDP Payload Length (in IPv4 checksum calculation)",
+            &format!("{}", UdpPayloadLengthIpv4)
+        );
+        assert_eq!(
+            "UDP Payload Length (in IPv6 checksum calculation)",
+            &format!("{}", UdpPayloadLengthIpv6)
+        );
+        assert_eq!(
+            "TCP Payload Length (in IPv4 checksum calculation)",
+            &format!("{}", TcpPayloadLengthIpv4)
+        );
+        assert_eq!(
+            "TCP Payload Length (in IPv6 checksum calculation)",
+            &format!("{}", TcpPayloadLengthIpv6)
+        );
+        assert_eq!("ICMPv6 Payload Length", &format!("{}", Icmpv6PayloadLength));
+    }
+}
diff --git a/src/helpers.rs b/src/helpers.rs
new file mode 100644
index 0000000..fc821ca
--- /dev/null
+++ b/src/helpers.rs
@@ -0,0 +1,104 @@
+/// Helper function for reading big endian u16 values from a ptr unchecked.
+///
+/// # Safety
+///
+/// It is in the responsibility of the caller to ensure there are at least 2
+/// bytes accessable via the ptr. If this is not the case undefined behavior
+/// will be triggered.
+#[inline]
+pub(crate) unsafe fn get_unchecked_be_u16(ptr: *const u8) -> u16 {
+    u16::from_be_bytes([*ptr, *ptr.add(1)])
+}
+
+/// Helper function for reading big endian u32 values from a ptr unchecked.
+///
+/// # Safety
+///
+/// It is in the responsibility of the caller to ensure there are at least 4
+/// bytes accessable via the ptr. If this is not the case undefined behavior
+/// will be triggered.
+#[inline]
+pub(crate) unsafe fn get_unchecked_be_u32(ptr: *const u8) -> u32 {
+    u32::from_be_bytes([*ptr, *ptr.add(1), *ptr.add(2), *ptr.add(3)])
+}
+
+/// Helper function for reading a 4 byte fixed-size array.
+///
+/// # Safety
+///
+/// It is in the responsibility of the caller to ensure there are at least 4
+/// bytes accessable via the ptr. If this is not the case undefined behavior
+/// will be triggered.
+#[inline]
+pub(crate) unsafe fn get_unchecked_4_byte_array(ptr: *const u8) -> [u8; 4] {
+    [*ptr, *ptr.add(1), *ptr.add(2), *ptr.add(3)]
+}
+
+/// Helper function for reading a 6 byte fixed-size array.
+///
+/// # Safety
+///
+/// It is in the responsibility of the caller to ensure there are at least 6
+/// bytes accessable via the ptr. If this is not the case undefined behavior
+/// will be triggered.
+#[inline]
+pub(crate) unsafe fn get_unchecked_6_byte_array(ptr: *const u8) -> [u8; 6] {
+    [
+        *ptr,
+        *ptr.add(1),
+        *ptr.add(2),
+        *ptr.add(3),
+        *ptr.add(4),
+        *ptr.add(5),
+    ]
+}
+
+/// Helper function for reading a 8 byte fixed-size array.
+///
+/// # Safety
+///
+/// It is in the responsibility of the caller to ensure there are at least 6
+/// bytes accessable via the ptr. If this is not the case undefined behavior
+/// will be triggered.
+#[inline]
+pub(crate) unsafe fn get_unchecked_8_byte_array(ptr: *const u8) -> [u8; 8] {
+    [
+        *ptr,
+        *ptr.add(1),
+        *ptr.add(2),
+        *ptr.add(3),
+        *ptr.add(4),
+        *ptr.add(5),
+        *ptr.add(6),
+        *ptr.add(7),
+    ]
+}
+
+/// Helper function for reading a 16 byte fixed-size array.
+///
+/// # Safety
+///
+/// It is in the responsibility of the caller to ensure there are at least 16
+/// bytes accessable via the ptr. If this is not the case undefined behavior
+/// will be triggered.
+#[inline]
+pub(crate) unsafe fn get_unchecked_16_byte_array(ptr: *const u8) -> [u8; 16] {
+    [
+        *ptr,
+        *ptr.add(1),
+        *ptr.add(2),
+        *ptr.add(3),
+        *ptr.add(4),
+        *ptr.add(5),
+        *ptr.add(6),
+        *ptr.add(7),
+        *ptr.add(8),
+        *ptr.add(9),
+        *ptr.add(10),
+        *ptr.add(11),
+        *ptr.add(12),
+        *ptr.add(13),
+        *ptr.add(14),
+        *ptr.add(15),
+    ]
+}
diff --git a/src/io/limited_reader.rs b/src/io/limited_reader.rs
new file mode 100644
index 0000000..a753950
--- /dev/null
+++ b/src/io/limited_reader.rs
@@ -0,0 +1,275 @@
+use crate::{
+    err::{io::LimitedReadError, Layer, LenError},
+    *,
+};
+
+/// Encapsulated reader with an maximum allowed read length.
+///
+/// This struct is used to limit data reads by lower protocol layers
+/// (e.g. the payload_len in an IPv6Header limits how much data should
+/// be read by the following layers).
+///
+/// An [`crate::err::LenError`] is returned as soon as more than the
+/// maximum read len is read.
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub struct LimitedReader<T> {
+    /// Reader from which data will be read.
+    reader: T,
+    /// Maximum len that still can be read (on the current layer).
+    max_len: usize,
+    /// Source of the maximum length.
+    len_source: LenSource,
+    /// Layer that is currently read (used for len error).
+    layer: Layer,
+    /// Offset of the layer that is currently read (used for len error).
+    layer_offset: usize,
+    /// Len that was read on the current layer.
+    read_len: usize,
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl<T: std::io::Read + Sized> LimitedReader<T> {
+    /// Setup a new limited reader.
+    pub fn new(
+        reader: T,
+        max_len: usize,
+        len_source: LenSource,
+        layer_offset: usize,
+        layer: Layer,
+    ) -> LimitedReader<T> {
+        LimitedReader {
+            reader,
+            max_len,
+            len_source,
+            layer,
+            layer_offset,
+            read_len: 0,
+        }
+    }
+
+    /// Maximum len that still can be read (on the current layer).
+    pub fn max_len(&self) -> usize {
+        self.max_len
+    }
+
+    /// Source of the maximum length (used for len error).
+    pub fn len_source(&self) -> LenSource {
+        self.len_source
+    }
+
+    /// Layer that is currently read (used for len error).
+    pub fn layer(&self) -> Layer {
+        self.layer
+    }
+
+    /// Offset of the layer that is currently read (used for len error).
+    pub fn layer_offset(&self) -> usize {
+        self.layer_offset
+    }
+
+    /// Len that was read on the current layer.
+    pub fn read_len(&self) -> usize {
+        self.read_len
+    }
+
+    /// Set current position as starting position for a layer.
+    pub fn start_layer(&mut self, layer: Layer) {
+        self.layer_offset += self.read_len;
+        self.max_len -= self.read_len;
+        self.read_len = 0;
+        self.layer = layer;
+    }
+
+    /// Try read the given buf length from the reader.
+    ///
+    /// Triggers an len error if the
+    pub fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), LimitedReadError> {
+        use LimitedReadError::*;
+        if self.max_len - self.read_len < buf.len() {
+            Err(Len(LenError {
+                required_len: self.read_len + buf.len(),
+                len: self.max_len,
+                len_source: self.len_source,
+                layer: self.layer,
+                layer_start_offset: self.layer_offset,
+            }))
+        } else {
+            self.reader.read_exact(buf).map_err(Io)?;
+            self.read_len += buf.len();
+            Ok(())
+        }
+    }
+
+    /// Consumes LimitedReader and returns the reader.
+    pub fn take_reader(self) -> T {
+        self.reader
+    }
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl<T: core::fmt::Debug> core::fmt::Debug for LimitedReader<T> {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        f.debug_struct("LimitedReader")
+            .field("reader", &self.reader)
+            .field("max_len", &self.max_len)
+            .field("len_source", &self.len_source)
+            .field("layer", &self.layer)
+            .field("layer_offset", &self.layer_offset)
+            .field("read_len", &self.read_len)
+            .finish()
+    }
+}
+
+#[cfg(all(test, feature = "std"))]
+mod tests {
+    use std::format;
+    use std::io::Cursor;
+
+    use super::*;
+
+    #[test]
+    fn new() {
+        let data = [1, 2, 3, 4];
+        let actual = LimitedReader::new(
+            Cursor::new(&data),
+            data.len(),
+            LenSource::Slice,
+            5,
+            Layer::Ipv4Header,
+        );
+        assert_eq!(actual.max_len, data.len());
+        assert_eq!(actual.max_len(), data.len());
+        assert_eq!(actual.len_source, LenSource::Slice);
+        assert_eq!(actual.len_source(), LenSource::Slice);
+        assert_eq!(actual.layer, Layer::Ipv4Header);
+        assert_eq!(actual.layer(), Layer::Ipv4Header);
+        assert_eq!(actual.layer_offset, 5);
+        assert_eq!(actual.layer_offset(), 5);
+        assert_eq!(actual.read_len, 0);
+        assert_eq!(actual.read_len(), 0);
+    }
+
+    #[test]
+    fn start_layer() {
+        let data = [1, 2, 3, 4, 5];
+        let mut r = LimitedReader::new(
+            Cursor::new(&data),
+            data.len(),
+            LenSource::Slice,
+            6,
+            Layer::Ipv4Header,
+        );
+        {
+            let mut read_result = [0u8; 2];
+            r.read_exact(&mut read_result).unwrap();
+            assert_eq!(read_result, [1, 2]);
+        }
+        r.start_layer(Layer::IpAuthHeader);
+
+        assert_eq!(r.max_len, 3);
+        assert_eq!(r.len_source, LenSource::Slice);
+        assert_eq!(r.layer, Layer::IpAuthHeader);
+        assert_eq!(r.layer_offset, 2 + 6);
+        assert_eq!(r.read_len, 0);
+
+        {
+            let mut read_result = [0u8; 4];
+            assert_eq!(
+                r.read_exact(&mut read_result).unwrap_err().len().unwrap(),
+                LenError {
+                    required_len: 4,
+                    len: 3,
+                    len_source: LenSource::Slice,
+                    layer: Layer::IpAuthHeader,
+                    layer_start_offset: 2 + 6
+                }
+            );
+        }
+    }
+
+    #[test]
+    fn read_exact() {
+        let data = [1, 2, 3, 4, 5];
+        let mut r = LimitedReader::new(
+            Cursor::new(&data),
+            data.len() + 1,
+            LenSource::Ipv4HeaderTotalLen,
+            10,
+            Layer::Ipv4Header,
+        );
+
+        // normal read
+        {
+            let mut read_result = [0u8; 2];
+            r.read_exact(&mut read_result).unwrap();
+            assert_eq!(read_result, [1, 2]);
+        }
+
+        // len error
+        {
+            let mut read_result = [0u8; 5];
+            assert_eq!(
+                r.read_exact(&mut read_result).unwrap_err().len().unwrap(),
+                LenError {
+                    required_len: 7,
+                    len: 6,
+                    len_source: LenSource::Ipv4HeaderTotalLen,
+                    layer: Layer::Ipv4Header,
+                    layer_start_offset: 10
+                }
+            );
+        }
+
+        // io error
+        {
+            let mut read_result = [0u8; 4];
+            assert!(r.read_exact(&mut read_result).unwrap_err().io().is_some());
+        }
+    }
+
+    #[test]
+    fn take_reader() {
+        let data = [1, 2, 3, 4, 5];
+        let mut r = LimitedReader::new(
+            Cursor::new(&data),
+            data.len(),
+            LenSource::Slice,
+            6,
+            Layer::Ipv4Header,
+        );
+        {
+            let mut read_result = [0u8; 2];
+            r.read_exact(&mut read_result).unwrap();
+            assert_eq!(read_result, [1, 2]);
+        }
+        let result = r.take_reader();
+        assert_eq!(2, result.position());
+    }
+
+    #[test]
+    fn debug() {
+        let data = [1, 2, 3, 4];
+        let actual = LimitedReader::new(
+            Cursor::new(&data),
+            data.len(),
+            LenSource::Slice,
+            5,
+            Layer::Ipv4Header,
+        );
+        assert_eq!(
+            format!("{:?}", actual),
+            format!(
+                "LimitedReader {{ reader: {:?}, max_len: {:?}, len_source: {:?}, layer: {:?}, layer_offset: {:?}, read_len: {:?} }}",
+                &actual.reader,
+                &actual.max_len,
+                &actual.len_source,
+                &actual.layer,
+                &actual.layer_offset,
+                &actual.read_len
+            )
+        );
+    }
+}
diff --git a/src/io/mod.rs b/src/io/mod.rs
new file mode 100644
index 0000000..b40053a
--- /dev/null
+++ b/src/io/mod.rs
@@ -0,0 +1,4 @@
+#[cfg(feature = "std")]
+mod limited_reader;
+#[cfg(feature = "std")]
+pub use limited_reader::*;
diff --git a/src/lax_packet_headers.rs b/src/lax_packet_headers.rs
new file mode 100644
index 0000000..42f4df6
--- /dev/null
+++ b/src/lax_packet_headers.rs
@@ -0,0 +1,1700 @@
+use crate::{
+    err::{packet::SliceError, Layer, LenError},
+    *,
+};
+
+/// Decoded packet headers (data link layer and lower) with lax length checks.
+///
+/// You can use
+///
+/// * [`LaxPacketHeaders::from_ethernet`]
+/// * [`LaxPacketHeaders::from_ether_type`]
+/// * [`LaxPacketHeaders::from_ip`]
+///
+/// depending on your starting header to parse the headers in a slice and get this
+/// struct as a result.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct LaxPacketHeaders<'a> {
+    /// Ethernet II header if present.
+    pub link: Option<LinkHeader>,
+
+    /// Single or double vlan headers if present.
+    pub vlan: Option<VlanHeader>,
+
+    /// IPv4 or IPv6 header and IP extension headers if present.
+    pub net: Option<NetHeaders>,
+
+    /// TCP or UDP header if present.
+    pub transport: Option<TransportHeader>,
+
+    /// Payload of the last parsed layer.
+    pub payload: LaxPayloadSlice<'a>,
+
+    /// Error that stopped the parsing and the layer on which the stop occurred.
+    pub stop_err: Option<(err::packet::SliceError, Layer)>,
+}
+
+impl<'a> LaxPacketHeaders<'a> {
+    /// Separates a network packet into different headers from the ethernet header
+    /// downwards with lax length checks and non-terminating errors.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    ///```
+    /// # use etherparse::{Ethernet2Header, PacketBuilder};
+    /// # let builder = PacketBuilder::
+    /// #    ethernet2([1,2,3,4,5,6],     //source mac
+    /// #               [7,8,9,10,11,12]) //destionation 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
+    /// # // payload of the udp packet
+    /// # let payload = [1,2,3,4,5,6,7,8];
+    /// # // get some memory to store the serialized data
+    /// # let mut packet = Vec::<u8>::with_capacity(
+    /// #     builder.size(payload.len())
+    /// # );
+    /// # builder.write(&mut packet, &payload).unwrap();
+    /// #
+    /// use etherparse::{ether_type, LaxPacketHeaders, LenSource, LaxPayloadSlice};
+    ///
+    /// match LaxPacketHeaders::from_ethernet(&packet) {
+    ///     Err(value) => {
+    ///         // An error is returned in case the ethernet II header could
+    ///         // not be parsed (other errors are stored in the "stop_err" field)
+    ///         println!("Err {:?}", value)
+    ///     },
+    ///     Ok(value) => {
+    ///         if let Some((stop_err, error_layer)) = value.stop_err.as_ref() {
+    ///             // error was encountered after parsing the ethernet 2 header
+    ///             println!("Error on layer {}: {:?}", error_layer, stop_err);
+    ///         }
+    ///
+    ///         // parts that could be parsed without error
+    ///         println!("link: {:?}", value.link);
+    ///         println!("vlan: {:?}", value.vlan);
+    ///         println!("net: {:?}", value.net);
+    ///         println!("transport: {:?}", value.transport);
+    ///
+    ///         // net (ip) & transport (udp or tcp)
+    ///         println!("net: {:?}", value.net);
+    ///         match value.payload {
+    ///             LaxPayloadSlice::Ether(e) => {
+    ///                 println!("ether payload (ether type {:?}): {:?}", e.ether_type, e.payload);
+    ///             }
+    ///             LaxPayloadSlice::Ip(ip) => {
+    ///                 println!("IP payload (IP number {:?}): {:?}", ip.ip_number, ip.payload);
+    ///                 if ip.incomplete {
+    ///                     println!("  IP payload incomplete (length in IP header indicated more data should be present)");
+    ///                 }
+    ///                 if ip.fragmented {
+    ///                     println!("  IP payload fragmented");
+    ///                 }
+    ///             }
+    ///             LaxPayloadSlice::Udp{ payload, incomplete } => {
+    ///                 println!("UDP payload: {:?}", payload);
+    ///                 if incomplete {
+    ///                     println!("  UDP payload incomplete (length in UDP or IP header indicated more data should be present)");
+    ///                 }
+    ///             }
+    ///             LaxPayloadSlice::Tcp{ payload, incomplete } => {
+    ///                 println!("TCP payload: {:?}", payload);
+    ///                 if incomplete {
+    ///                     println!("  TCP payload incomplete (length in IP header indicated more data should be present)");
+    ///                 }
+    ///             }
+    ///             LaxPayloadSlice::Icmpv4{ payload, incomplete } => {
+    ///                 println!("Icmpv4 payload: {:?}", payload);
+    ///                 if incomplete {
+    ///                     println!("  Icmpv4 payload incomplete (length in IP header indicated more data should be present)");
+    ///                 }
+    ///             }
+    ///             LaxPayloadSlice::Icmpv6{ payload, incomplete } => {
+    ///                 println!("Icmpv6 payload: {:?}", payload);
+    ///                 if incomplete {
+    ///                     println!("  Icmpv6 payload incomplete (length in IP header indicated more data should be present)");
+    ///                 }
+    ///             }
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// ```
+    pub fn from_ethernet(slice: &'a [u8]) -> Result<LaxPacketHeaders<'a>, err::LenError> {
+        let (ethernet, rest) = Ethernet2Header::from_slice(slice)?;
+        let mut result = Self::from_ether_type(ethernet.ether_type, rest);
+        result.link = Some(LinkHeader::Ethernet2(ethernet));
+        if let Some((SliceError::Len(l), _)) = result.stop_err.as_mut() {
+            l.layer_start_offset += Ethernet2Header::LEN;
+        }
+        Ok(result)
+    }
+
+    /// Separates a network packet into different headers using
+    /// the given `ether_type` number to identify the first header with lax length
+    /// checks and non-terminating errors.
+    ///
+    /// The result is returned as a [`LaxSlicedPacket`] struct. Currently supported
+    /// ether type numbers are:
+    ///
+    /// * `ether_type::IPV4`
+    /// * `ether_type::IPV6`
+    /// * `ether_type::VLAN_TAGGED_FRAME`
+    /// * `ether_type::PROVIDER_BRIDGING`
+    /// * `ether_type::VLAN_DOUBLE_TAGGED_FRAME`
+    ///
+    /// If an unsupported ether type is given the given slice will be set as payload
+    /// and all other fields will be set to `None`.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    ///```
+    /// # use etherparse::{Ethernet2Header, PacketBuilder};
+    /// # let builder = PacketBuilder::
+    /// #    ethernet2([1,2,3,4,5,6],     //source mac
+    /// #               [7,8,9,10,11,12]) //destionation 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
+    /// # // payload of the udp packet
+    /// # let payload = [1,2,3,4,5,6,7,8];
+    /// # // get some memory to store the serialized data
+    /// # let mut complete_packet = Vec::<u8>::with_capacity(
+    /// #     builder.size(payload.len())
+    /// # );
+    /// # builder.write(&mut complete_packet, &payload).unwrap();
+    /// # // skip ethernet 2 header so we can parse from there downwards
+    /// # let packet = &complete_packet[Ethernet2Header::LEN..];
+    /// #
+    /// use etherparse::{ether_type, LaxPacketHeaders, LenSource, LaxPayloadSlice};
+    ///
+    /// let value = LaxPacketHeaders::from_ether_type(ether_type::IPV4, &packet);
+    ///
+    /// if let Some((stop_err, error_layer)) = value.stop_err.as_ref() {
+    ///     // error was encountered after parsing the ethernet 2 header
+    ///     println!("Error on layer {}: {:?}", error_layer, stop_err);
+    /// }
+    ///
+    /// // link is unfilled
+    /// assert_eq!(value.link, None);
+    ///
+    /// // parts that could be parsed without error
+    /// println!("vlan: {:?}", value.vlan);
+    /// println!("net: {:?}", value.net);
+    /// println!("transport: {:?}", value.transport);
+    ///
+    /// // net (ip) & transport (udp or tcp)
+    /// println!("net: {:?}", value.net);
+    /// match value.payload {
+    ///     LaxPayloadSlice::Ether(e) => {
+    ///         println!("ether payload (ether type {:?}): {:?}", e.ether_type, e.payload);
+    ///     }
+    ///     LaxPayloadSlice::Ip(ip) => {
+    ///         println!("IP payload (IP number {:?}): {:?}", ip.ip_number, ip.payload);
+    ///         if ip.incomplete {
+    ///             println!("  IP payload incomplete (length in IP header indicated more data should be present)");
+    ///         }
+    ///         if ip.fragmented {
+    ///             println!("  IP payload fragmented");
+    ///         }
+    ///     }
+    ///     LaxPayloadSlice::Udp{ payload, incomplete } => {
+    ///         println!("UDP payload: {:?}", payload);
+    ///         if incomplete {
+    ///             println!("  UDP payload incomplete (length in UDP or IP header indicated more data should be present)");
+    ///         }
+    ///     }
+    ///     LaxPayloadSlice::Tcp{ payload, incomplete } => {
+    ///         println!("TCP payload: {:?}", payload);
+    ///         if incomplete {
+    ///             println!("  TCP payload incomplete (length in IP header indicated more data should be present)");
+    ///         }
+    ///     }
+    ///     LaxPayloadSlice::Icmpv4{ payload, incomplete } => {
+    ///         println!("Icmpv4 payload: {:?}", payload);
+    ///         if incomplete {
+    ///             println!("  Icmpv4 payload incomplete (length in IP header indicated more data should be present)");
+    ///         }
+    ///     }
+    ///     LaxPayloadSlice::Icmpv6{ payload, incomplete } => {
+    ///         println!("Icmpv6 payload: {:?}", payload);
+    ///         if incomplete {
+    ///             println!("  Icmpv6 payload incomplete (length in IP header indicated more data should be present)");
+    ///         }
+    ///     }
+    /// }
+    /// ```
+    pub fn from_ether_type(mut ether_type: EtherType, slice: &'a [u8]) -> LaxPacketHeaders<'a> {
+        use err::packet::SliceError::*;
+
+        let mut rest = slice;
+        let mut offset = 0;
+        let mut result = LaxPacketHeaders {
+            link: None,
+            vlan: None,
+            net: None,
+            transport: None,
+            payload: LaxPayloadSlice::Ether(EtherPayloadSlice {
+                ether_type,
+                payload: rest,
+            }),
+            stop_err: None,
+        };
+
+        // parse vlan header(s)
+        use ether_type::*;
+
+        result.vlan = match ether_type {
+            VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => {
+                use crate::VlanHeader::*;
+                let (outer, outer_rest) = 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 = outer_rest;
+                offset += SingleVlanHeader::LEN;
+                ether_type = outer.ether_type;
+                result.payload = LaxPayloadSlice::Ether(EtherPayloadSlice {
+                    ether_type,
+                    payload: rest,
+                });
+
+                // parse second vlan header if present
+                match ether_type {
+                    // second vlan tagging header
+                    VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => {
+                        let (inner, inner_rest) = 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;
+                            }
+                        };
+
+                        // 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,
+                        });
+
+                        Some(Double(DoubleVlanHeader { outer, inner }))
+                    }
+                    // no second vlan header detected -> single vlan header
+                    _ => Some(Single(outer)),
+                }
+            }
+            // no vlan header
+            _ => None,
+        };
+
+        // parse ip
+        match ether_type {
+            IPV4 | IPV6 => match result.add_ip(offset, rest) {
+                Ok(_) => {}
+                Err(err) => {
+                    use err::ip::LaxHeaderSliceError as I;
+                    result.stop_err = Some(match err {
+                        I::Len(mut l) => {
+                            l.layer_start_offset += offset;
+                            (Len(l), Layer::IpHeader)
+                        }
+                        I::Content(c) => (Ip(c), Layer::IpHeader),
+                    });
+                    return result;
+                }
+            },
+            _ => {}
+        };
+
+        result
+    }
+
+    /// Separates a network packet slice into different headers from the
+    /// ip header downwards with lax length checks and will still return
+    /// a result even if an error is encountered in a layer (except IP).
+    ///
+    /// This function has two main differences to [`PacketHeaders::from_ip_slice`]:
+    ///
+    /// * Errors encountered bellow the IpHeader will only stop the parsing and
+    ///   return an `Ok` 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.
+    /// * Length in the IP header & UDP headers are allowed to be inconsistent with the
+    ///   given slice length (e.g. data is missing from the slice). In this case it falls
+    ///   back to the length of slice. See [`LaxIpSlice::from_slice`] for a detailed
+    ///   description of when the slice length is used as a fallback.
+    ///
+    /// The result is returned as a [`SlicedPacket`] struct. This function
+    /// assumes the given data starts with an IPv4 or IPv6 header.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    ///```
+    /// # use etherparse::{PacketBuilder, Ethernet2Header};
+    /// # let builder = PacketBuilder::
+    /// #    ipv4([192,168,1,1], //source ip
+    /// #         [192,168,1,2], //destination ip
+    /// #         20)            //time to life
+    /// #    .udp(21,    //source port
+    /// #         1234); //desitnation port
+    /// # //payload of the udp packet
+    /// # let payload = [1,2,3,4,5,6,7,8];
+    /// # // get some memory to store the serialized data
+    /// # let mut complete_packet = Vec::<u8>::with_capacity(
+    /// #     builder.size(payload.len())
+    /// # );
+    /// # builder.write(&mut complete_packet, &payload).unwrap();
+    /// # // skip ethernet 2 header so we can parse from there downwards
+    /// # let packet = &complete_packet[Ethernet2Header::LEN..];
+    /// #
+    /// use etherparse::{ether_type, LaxPacketHeaders, LenSource, LaxPayloadSlice};
+    ///
+    /// match LaxPacketHeaders::from_ip(&packet) {
+    ///     Err(value) => {
+    ///         // An error is returned in case the ip header could
+    ///         // not be parsed (other errors are stored in the "stop_err" field)
+    ///         println!("Err {:?}", value)
+    ///     },
+    ///     Ok(value) => {
+    ///         if let Some((stop_err, error_layer)) = value.stop_err.as_ref() {
+    ///             // error was encountered after parsing the ethernet 2 header
+    ///             println!("Error on layer {}: {:?}", error_layer, stop_err);
+    ///         }
+    ///
+    ///         // link & vlan is unfilled
+    ///         assert_eq!(value.link, None);
+    ///         assert_eq!(value.vlan, None);
+    ///
+    ///         // parts that could be parsed without error
+    ///         println!("net: {:?}", value.net);
+    ///         println!("transport: {:?}", value.transport);
+    ///
+    ///         // net (ip) & transport (udp or tcp)
+    ///         println!("net: {:?}", value.net);
+    ///         match value.payload {
+    ///             // if you parse from IP down there will be no ether payload
+    ///             LaxPayloadSlice::Ether(e) => unreachable!(),
+    ///             LaxPayloadSlice::Ip(ip) => {
+    ///                 println!("IP payload (IP number {:?}): {:?}", ip.ip_number, ip.payload);
+    ///                 if ip.incomplete {
+    ///                     println!("  IP payload incomplete (length in IP header indicated more data should be present)");
+    ///                 }
+    ///                 if ip.fragmented {
+    ///                     println!("  IP payload fragmented");
+    ///                 }
+    ///             }
+    ///             LaxPayloadSlice::Udp{ payload, incomplete } => {
+    ///                 println!("UDP payload: {:?}", payload);
+    ///                 if incomplete {
+    ///                     println!("  UDP payload incomplete (length in UDP or IP header indicated more data should be present)");
+    ///                 }
+    ///             }
+    ///             LaxPayloadSlice::Tcp{ payload, incomplete } => {
+    ///                 println!("TCP payload: {:?}", payload);
+    ///                 if incomplete {
+    ///                     println!("  TCP payload incomplete (length in IP header indicated more data should be present)");
+    ///                 }
+    ///             }
+    ///             LaxPayloadSlice::Icmpv4{ payload, incomplete } => {
+    ///                 println!("Icmpv4 payload: {:?}", payload);
+    ///                 if incomplete {
+    ///                     println!("  Icmpv4 payload incomplete (length in IP header indicated more data should be present)");
+    ///                 }
+    ///             }
+    ///             LaxPayloadSlice::Icmpv6{ payload, incomplete } => {
+    ///                 println!("Icmpv6 payload: {:?}", payload);
+    ///                 if incomplete {
+    ///                     println!("  Icmpv6 payload incomplete (length in IP header indicated more data should be present)");
+    ///                 }
+    ///             }
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// ```
+    pub fn from_ip(slice: &'a [u8]) -> Result<LaxPacketHeaders<'a>, err::ip::LaxHeaderSliceError> {
+        let mut result = Self {
+            link: None,
+            vlan: None,
+            net: None,
+            transport: None,
+            // dummy initialize (will be overwritten if add_ip is successfull)
+            payload: LaxPayloadSlice::Udp {
+                payload: &[],
+                incomplete: true,
+            },
+            stop_err: None,
+        };
+        result.add_ip(0, slice)?;
+        Ok(result)
+    }
+
+    fn add_ip(
+        &mut self,
+        offset: usize,
+        slice: &'a [u8],
+    ) -> Result<(), err::ip::LaxHeaderSliceError> {
+        use err::packet::SliceError::*;
+
+        // read ipv4 header & extensions and payload slice
+        let (ip, ip_payload, stop_err) = IpHeaders::from_slice_lax(slice)?;
+
+        // set the next
+        self.net = Some(ip.into());
+        self.payload = LaxPayloadSlice::Ip(ip_payload.clone());
+
+        // if a stop error was encountered return it
+        if let Some((err, layer)) = stop_err {
+            use err::ip_exts::HeaderError as IC;
+            use err::ip_exts::HeadersSliceError as I;
+
+            self.stop_err = Some((
+                match err {
+                    I::Len(mut l) => {
+                        l.layer_start_offset += offset;
+                        l.len_source = ip_payload.len_source;
+                        Len(l)
+                    }
+                    I::Content(e) => match e {
+                        IC::Ipv4Ext(e) => SliceError::Ipv4Exts(e),
+                        IC::Ipv6Ext(e) => SliceError::Ipv6Exts(e),
+                    },
+                },
+                layer,
+            ));
+            return Ok(());
+        }
+
+        // update the offset with the ip headers
+        let offset = offset + ((ip_payload.payload.as_ptr() as usize) - (slice.as_ptr() as usize));
+
+        // decode transport layer
+        if false == ip_payload.fragmented {
+            // helper function to set the len source in len errors
+            let add_len_source = |mut len_error: LenError| -> err::packet::SliceError {
+                // only change the len source if the lower layer has not set it
+                if LenSource::Slice == len_error.len_source {
+                    len_error.len_source = ip_payload.len_source;
+                    len_error.layer_start_offset += offset;
+                }
+                err::packet::SliceError::Len(len_error)
+            };
+
+            use crate::ip_number::*;
+            use err::tcp::HeaderSliceError::*;
+            match ip_payload.ip_number {
+                ICMP => match Icmpv4Slice::from_slice(ip_payload.payload) {
+                    Ok(i) => {
+                        self.transport = Some(TransportHeader::Icmpv4(i.header()));
+                        self.payload = LaxPayloadSlice::Icmpv4 {
+                            payload: i.payload(),
+                            incomplete: ip_payload.incomplete,
+                        };
+                    }
+                    Err(e) => {
+                        self.stop_err = Some((add_len_source(e), Layer::Icmpv4));
+                    }
+                },
+                IPV6_ICMP => match Icmpv6Slice::from_slice(ip_payload.payload) {
+                    Ok(i) => {
+                        self.transport = Some(TransportHeader::Icmpv6(i.header()));
+                        self.payload = LaxPayloadSlice::Icmpv6 {
+                            payload: i.payload(),
+                            incomplete: ip_payload.incomplete,
+                        };
+                    }
+                    Err(e) => {
+                        self.stop_err = Some((add_len_source(e), Layer::Icmpv6));
+                    }
+                },
+                UDP => {
+                    match UdpSlice::from_slice_lax(ip_payload.payload) {
+                        Ok(u) => {
+                            self.transport = Some(TransportHeader::Udp(u.to_header()));
+                            self.payload = LaxPayloadSlice::Udp {
+                                payload: u.payload(),
+                                // TODO also check the udp header length
+                                incomplete: ip_payload.incomplete,
+                            };
+                        }
+                        Err(e) => {
+                            self.stop_err = Some((add_len_source(e), Layer::UdpHeader));
+                        }
+                    }
+                }
+                TCP => match TcpHeader::from_slice(ip_payload.payload) {
+                    Ok(t) => {
+                        self.transport = Some(TransportHeader::Tcp(t.0));
+                        self.payload = LaxPayloadSlice::Tcp {
+                            payload: t.1,
+                            incomplete: ip_payload.incomplete,
+                        };
+                    }
+                    Err(e) => match e {
+                        Len(l) => {
+                            self.stop_err = Some((add_len_source(l), Layer::TcpHeader));
+                        }
+                        Content(c) => {
+                            self.stop_err = Some((SliceError::Tcp(c), Layer::TcpHeader));
+                        }
+                    },
+                },
+                _ => {}
+            }
+        }
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_packet::TestPacket;
+
+    const VLAN_ETHER_TYPES: [EtherType; 3] = [
+        ether_type::VLAN_TAGGED_FRAME,
+        ether_type::PROVIDER_BRIDGING,
+        ether_type::VLAN_DOUBLE_TAGGED_FRAME,
+    ];
+
+    #[test]
+    fn clone_eq() {
+        let header = LaxPacketHeaders {
+            link: None,
+            vlan: None,
+            net: None,
+            transport: None,
+            stop_err: None,
+            payload: LaxPayloadSlice::Udp {
+                payload: &[],
+                incomplete: false,
+            },
+        };
+        assert_eq!(header.clone(), header);
+    }
+
+    #[test]
+    fn debug() {
+        use alloc::format;
+        let payload = LaxPayloadSlice::Udp {
+            payload: &[],
+            incomplete: false,
+        };
+        let header = LaxPacketHeaders {
+            link: None,
+            vlan: None,
+            net: None,
+            transport: None,
+            payload: payload.clone(),
+            stop_err: None,
+        };
+        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
+            )
+        );
+    }
+
+    #[test]
+    fn from_x_slice() {
+        // no eth
+        from_x_slice_vlan_variants(&TestPacket {
+            link: None,
+            vlan: None,
+            net: None,
+            transport: None,
+        });
+
+        // eth
+        {
+            let eth = Ethernet2Header {
+                source: [1, 2, 3, 4, 5, 6],
+                destination: [1, 2, 3, 4, 5, 6],
+                ether_type: 0.into(),
+            };
+            let test = TestPacket {
+                link: Some(LinkHeader::Ethernet2(eth.clone())),
+                vlan: None,
+                net: None,
+                transport: None,
+            };
+
+            // ok ethernet header (with unknown next)
+            from_x_slice_vlan_variants(&test);
+
+            // eth len error
+            {
+                let data = test.to_vec(&[]);
+                for len in 0..data.len() {
+                    assert_test_result(&test, &[], &data[..len], None, None);
+                }
+            }
+        }
+
+        // unknown ether_type
+        {
+            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_eq!(None, actual.net);
+            assert_eq!(None, actual.transport);
+            assert_eq!(
+                actual.payload,
+                LaxPayloadSlice::Ether(EtherPayloadSlice {
+                    ether_type: 0.into(),
+                    payload: &payload
+                })
+            );
+            assert_eq!(None, actual.stop_err);
+        }
+    }
+
+    fn from_x_slice_vlan_variants(base: &TestPacket) {
+        // none
+        from_x_slice_ip_variants(base);
+
+        // single vlan header
+        {
+            let single = SingleVlanHeader {
+                pcp: 1.try_into().unwrap(),
+                drop_eligible_indicator: false,
+                vlan_id: 2.try_into().unwrap(),
+                ether_type: 3.into(),
+            };
+
+            for vlan_ether_type in VLAN_ETHER_TYPES {
+                let mut test = base.clone();
+                test.set_ether_type(vlan_ether_type);
+                test.vlan = Some(VlanHeader::Single(single.clone()));
+
+                // ok vlan header
+                from_x_slice_ip_variants(&test);
+
+                // len error
+                {
+                    let data = test.to_vec(&[]);
+                    for len in 0..single.header_len() {
+                        let base_len = test.len(&[]) - single.header_len();
+
+                        let err = LenError {
+                            required_len: single.header_len(),
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: Layer::VlanHeader,
+                            layer_start_offset: base_len,
+                        };
+                        assert_test_result(
+                            &test,
+                            &[],
+                            &data[..base_len + len],
+                            None,
+                            Some((SliceError::Len(err.clone()), Layer::VlanHeader)),
+                        );
+                    }
+                }
+            }
+        }
+
+        // double vlan header
+        for outer_vlan_ether_type in VLAN_ETHER_TYPES {
+            for inner_vlan_ether_type in VLAN_ETHER_TYPES {
+                let double = DoubleVlanHeader {
+                    outer: SingleVlanHeader {
+                        pcp: 1.try_into().unwrap(),
+                        drop_eligible_indicator: false,
+                        vlan_id: 2.try_into().unwrap(),
+                        ether_type: inner_vlan_ether_type,
+                    },
+                    inner: SingleVlanHeader {
+                        pcp: 1.try_into().unwrap(),
+                        drop_eligible_indicator: false,
+                        vlan_id: 2.try_into().unwrap(),
+                        ether_type: 3.into(),
+                    },
+                };
+                let mut test = base.clone();
+                test.set_ether_type(outer_vlan_ether_type);
+                test.vlan = Some(VlanHeader::Double(double.clone()));
+
+                // ok double vlan header
+                from_x_slice_ip_variants(&test);
+
+                // len error
+                {
+                    let data = test.to_vec(&[]);
+                    for len in 0..SingleVlanHeader::LEN {
+                        let base_len = test.len(&[]) - SingleVlanHeader::LEN;
+
+                        let err = LenError {
+                            required_len: SingleVlanHeader::LEN,
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: Layer::VlanHeader,
+                            layer_start_offset: base_len,
+                        };
+                        assert_test_result(
+                            &test,
+                            &[],
+                            &data[..base_len + len],
+                            None,
+                            Some((SliceError::Len(err.clone()), Layer::VlanHeader)),
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    fn from_x_slice_ip_variants(base: &TestPacket) {
+        // none
+        from_x_slice_transport_variants(base);
+
+        // ipv4
+        for fragmented in [false, true] {
+            let ipv4 = {
+                let mut ipv4 =
+                    Ipv4Header::new(0, 1, 2.into(), [3, 4, 5, 6], [7, 8, 9, 10]).unwrap();
+                ipv4.more_fragments = fragmented;
+                ipv4
+            };
+
+            {
+                let mut test = base.clone();
+                test.set_ether_type(ether_type::IPV4);
+                test.net = Some(NetHeaders::Ipv4(ipv4.clone(), Default::default()));
+                test.set_payload_len(0);
+
+                // ok ipv4
+                from_x_slice_transport_variants(&test);
+
+                // ipv4 len error
+                {
+                    let data = test.to_vec(&[]);
+                    for len in 0..ipv4.header_len() {
+                        let base_len = test.len(&[]) - ipv4.header_len();
+
+                        let err = LenError {
+                            required_len: if len < 1 { 1 } else { ipv4.header_len() },
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: if len < 1 {
+                                Layer::IpHeader
+                            } else {
+                                Layer::Ipv4Header
+                            },
+                            layer_start_offset: base_len,
+                        };
+
+                        assert_test_result(
+                            &test,
+                            &[],
+                            &data[..base_len + len],
+                            Some(err::ip::LaxHeaderSliceError::Len(err.clone())),
+                            Some((SliceError::Len(err.clone()), Layer::IpHeader)),
+                        );
+                    }
+                }
+
+                // ipv4 content error (ihl length too small)
+                {
+                    use err::ip::HeaderError::*;
+
+                    let mut data = test.to_vec(&[]);
+                    let ipv4_offset = data.len() - ipv4.header_len();
+
+                    // set the ihl to 0 to trigger a content error
+                    data[ipv4_offset] = 0b1111_0000 & data[ipv4_offset];
+
+                    assert_test_result(
+                        &test,
+                        &[],
+                        &data,
+                        Some(err::ip::LaxHeaderSliceError::Content(
+                            Ipv4HeaderLengthSmallerThanHeader { ihl: 0 },
+                        )),
+                        Some((
+                            SliceError::Ip(Ipv4HeaderLengthSmallerThanHeader { ihl: 0 }),
+                            Layer::IpHeader,
+                        )),
+                    );
+                }
+
+                // ipv 4total length too small (does not change the output)
+                {
+                    let mut data = test.to_vec(&[]);
+                    let ipv4_offset = data.len() - ipv4.header_len();
+
+                    // set the total length to 0 to trigger a content error
+                    data[ipv4_offset + 2] = 0;
+                    data[ipv4_offset + 3] = 0;
+
+                    let mut mod_test = test.clone();
+                    mod_test.net = Some({
+                        let (h, e) = test.net.as_ref().map(|v| v.ipv4_ref()).flatten().unwrap();
+                        let mut ipv4 = h.clone();
+                        ipv4.total_len = 0;
+                        NetHeaders::Ipv4(ipv4, e.clone())
+                    });
+
+                    assert_test_result(&mod_test, &[], &data, None, None);
+                }
+            }
+
+            // ipv4 extension content error
+            {
+                let auth = IpAuthHeader::new(0.into(), 1, 2, &[]).unwrap();
+
+                let mut test = base.clone();
+                test.set_ether_type(ether_type::IPV4);
+                test.net = Some(NetHeaders::Ipv4(
+                    {
+                        let mut ipv4 = ipv4.clone();
+                        ipv4.protocol = ip_number::AUTH;
+                        ipv4
+                    },
+                    Ipv4Extensions {
+                        auth: Some(auth.clone()),
+                    },
+                ));
+                test.set_payload_len(0);
+
+                // ok ipv4 & extension
+                from_x_slice_transport_variants(&test);
+
+                // ipv4 extension len error
+                for len in 0..auth.header_len() {
+                    // set payload length
+                    let mut test = test.clone();
+                    test.set_payload_le_from_ip_on(
+                        -1 * (auth.header_len() as isize) + (len as isize),
+                    );
+
+                    let data = test.to_vec(&[]);
+                    let base_len = test.len(&[]) - auth.header_len();
+
+                    let err = LenError {
+                        required_len: auth.header_len(),
+                        len,
+                        len_source: LenSource::Ipv4HeaderTotalLen,
+                        layer: Layer::IpAuthHeader,
+                        layer_start_offset: base_len,
+                    };
+
+                    assert_test_result(
+                        &test,
+                        &[],
+                        &data,
+                        None,
+                        Some((SliceError::Len(err.clone()), Layer::IpAuthHeader)),
+                    );
+                }
+
+                // ipv4 extension content error
+                {
+                    let mut data = test.to_vec(&[]);
+                    let auth_offset = data.len() - auth.header_len();
+
+                    // set the icv len too smaller then allowed
+                    data[auth_offset + 1] = 0;
+
+                    // expect an error
+                    assert_test_result(
+                        &test,
+                        &[],
+                        &data,
+                        None,
+                        Some((
+                            SliceError::Ipv4Exts(err::ip_auth::HeaderError::ZeroPayloadLen),
+                            Layer::IpAuthHeader,
+                        )),
+                    );
+                }
+            }
+        }
+
+        // ipv6
+        {
+            let ipv6 = Ipv6Header {
+                traffic_class: 0,
+                flow_label: 1.try_into().unwrap(),
+                payload_length: 2,
+                next_header: 3.into(),
+                hop_limit: 4,
+                source: [0; 16],
+                destination: [0; 16],
+            };
+
+            // ipv6 header only
+            {
+                let mut test = base.clone();
+                test.set_ether_type(ether_type::IPV6);
+                test.net = Some(NetHeaders::Ipv6(ipv6.clone(), Default::default()));
+                test.set_payload_len(0);
+
+                // ok ipv6
+                from_x_slice_transport_variants(&test);
+
+                // header len ipv6
+                {
+                    let data = test.to_vec(&[]);
+                    for len in 0..ipv6.header_len() {
+                        let base_len = test.len(&[]) - ipv6.header_len();
+
+                        let err = err::LenError {
+                            required_len: if len < 1 { 1 } else { ipv6.header_len() },
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: if len < 1 {
+                                Layer::IpHeader
+                            } else {
+                                Layer::Ipv6Header
+                            },
+                            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 < 1 {
+                                        let mut err = err.clone();
+                                        err.required_len = 1;
+                                        err.layer = Layer::IpHeader;
+                                        err
+                                    } else {
+                                        err.clone()
+                                    }
+                                }),
+                                Layer::IpHeader,
+                            )),
+                        );
+                    }
+                }
+
+                // content error ipv6
+                {
+                    use err::ip::{HeaderError::*, LaxHeaderSliceError::Content};
+
+                    let mut data = test.to_vec(&[]);
+
+                    // inject an invalid ip version
+                    let base_len = data.len() - ipv6.header_len();
+                    data[base_len] = data[base_len] & 0b0000_1111;
+
+                    assert_test_result(
+                        &test,
+                        &[],
+                        &data,
+                        Some(Content(UnsupportedIpVersion { version_number: 0 })),
+                        Some((
+                            SliceError::Ip(UnsupportedIpVersion { version_number: 0 }),
+                            Layer::IpHeader,
+                        )),
+                    );
+                }
+            }
+
+            // ipv6 + extension
+            for fragment in [false, true] {
+                let auth = IpAuthHeader::new(ip_number::GGP, 1, 2, &[]).unwrap();
+                let frag = Ipv6FragmentHeader {
+                    next_header: ip_number::AUTH,
+                    fragment_offset: 0.try_into().unwrap(),
+                    more_fragments: fragment,
+                    identification: 3,
+                };
+
+                let mut test = base.clone();
+                test.set_ether_type(ether_type::IPV6);
+                test.net = Some(NetHeaders::Ipv6(
+                    {
+                        let mut ipv6 = ipv6.clone();
+                        ipv6.next_header = ip_number::IPV6_FRAG;
+                        ipv6
+                    },
+                    {
+                        let mut exts: Ipv6Extensions = Default::default();
+                        exts.fragment = Some(frag.clone());
+                        exts.auth = Some(auth.clone());
+                        exts
+                    },
+                ));
+                test.set_payload_len(0);
+
+                // ok ipv6 & extensions
+                from_x_slice_transport_variants(&test);
+
+                // ipv6 extension len error
+                for len in 0..auth.header_len() {
+                    // set payload length
+                    let mut test = test.clone();
+                    test.set_payload_le_from_ip_on(
+                        -1 * (auth.header_len() as isize) + (len as isize),
+                    );
+
+                    let data = test.to_vec(&[]);
+                    let base_len = test.len(&[]) - auth.header_len();
+
+                    let err = LenError {
+                        required_len: auth.header_len(),
+                        len,
+                        len_source: LenSource::Ipv6HeaderPayloadLen,
+                        layer: Layer::IpAuthHeader,
+                        layer_start_offset: base_len,
+                    };
+                    assert_test_result(
+                        &test,
+                        &[],
+                        &data[..base_len + len],
+                        None,
+                        Some((SliceError::Len(err.clone()), Layer::IpAuthHeader)),
+                    );
+                }
+
+                // ipv6 extension content error (auth)
+                {
+                    let mut data = test.to_vec(&[]);
+                    let auth_offset = data.len() - auth.header_len();
+                    // set the icv len too smaller then allowed
+                    data[auth_offset + 1] = 0;
+
+                    assert_test_result(
+                        &test,
+                        &[],
+                        &data,
+                        None,
+                        Some((
+                            SliceError::Ipv6Exts(err::ipv6_exts::HeaderError::IpAuth(
+                                err::ip_auth::HeaderError::ZeroPayloadLen,
+                            )),
+                            Layer::IpAuthHeader,
+                        )),
+                    );
+                }
+
+                // ipv6 extension content error (hop by hop not at start)
+                {
+                    let mut data = test.to_vec(&[]);
+                    let auth_offset = data.len() - auth.header_len();
+
+                    // set the next header to be a hop-by-hop header to trigger a "not at start error"
+                    data[auth_offset] = 0;
+
+                    assert_test_result(
+                        &test,
+                        &[],
+                        &data,
+                        None,
+                        Some((
+                            SliceError::Ipv6Exts(err::ipv6_exts::HeaderError::HopByHopNotAtStart),
+                            Layer::Ipv6HopByHopHeader,
+                        )),
+                    );
+                }
+            }
+        }
+    }
+
+    fn from_x_slice_transport_variants(base: &TestPacket) {
+        // none
+        from_x_slice_assert_ok(base);
+
+        // transport can only be set if ip is present
+        if let Some(ip) = &base.net {
+            // udp
+            {
+                let udp = UdpHeader {
+                    source_port: 1,
+                    destination_port: 2,
+                    length: 3,
+                    checksum: 4,
+                };
+                let mut test = base.clone();
+                test.net = Some({
+                    let mut ip = match ip {
+                        NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
+                        NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
+                    };
+                    ip.set_next_headers(ip_number::UDP);
+                    ip.into()
+                });
+                test.transport = Some(TransportHeader::Udp(udp.clone()));
+                test.set_payload_len(0);
+
+                // ok decode
+                from_x_slice_assert_ok(&test);
+
+                // length error
+                if false == test.is_ip_payload_fragmented() {
+                    for len in 0..udp.header_len() {
+                        // build new test packet
+                        let mut test = test.clone();
+
+                        // set payload length
+                        test.set_payload_le_from_ip_on(len as isize);
+
+                        // generate data
+                        let data = test.to_vec(&[]);
+
+                        let base_len = test.len(&[]) - udp.header_len();
+                        let err = LenError {
+                            required_len: udp.header_len(),
+                            len,
+                            len_source: match test.net.as_ref().unwrap() {
+                                NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
+                                NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
+                            },
+                            layer: Layer::UdpHeader,
+                            layer_start_offset: base_len,
+                        };
+                        assert_test_result(
+                            &test,
+                            &[],
+                            &data[..base_len + len],
+                            None,
+                            Some((SliceError::Len(err.clone()), Layer::UdpHeader)),
+                        );
+                    }
+                }
+            }
+
+            // tcp
+            {
+                let tcp = TcpHeader::new(1, 2, 3, 4);
+                let mut test = base.clone();
+                test.net = Some({
+                    let mut ip = match ip {
+                        NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
+                        NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
+                    };
+                    ip.set_next_headers(ip_number::TCP);
+                    ip.into()
+                });
+                test.transport = Some(TransportHeader::Tcp(tcp.clone()));
+                test.set_payload_len(0);
+
+                // ok decode
+                from_x_slice_assert_ok(&test);
+
+                // error can only occur if ip does not fragment the packet
+                if false == test.is_ip_payload_fragmented() {
+                    // length error
+                    {
+                        for len in 0..(tcp.header_len() as usize) {
+                            // set payload length
+                            let mut test = test.clone();
+                            test.set_payload_le_from_ip_on(len as isize);
+
+                            let data = test.to_vec(&[]);
+                            let base_len = test.len(&[]) - (tcp.header_len() as usize);
+
+                            let err = LenError {
+                                required_len: tcp.header_len() as usize,
+                                len,
+                                len_source: match test.net.as_ref().unwrap() {
+                                    NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
+                                    NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
+                                },
+                                layer: Layer::TcpHeader,
+                                layer_start_offset: base_len,
+                            };
+                            assert_test_result(
+                                &test,
+                                &[],
+                                &data[..base_len + len],
+                                None,
+                                Some((SliceError::Len(err.clone()), Layer::TcpHeader)),
+                            );
+                        }
+                    }
+
+                    // content error
+                    {
+                        let mut data = test.to_vec(&[]);
+                        let base_len = test.len(&[]) - (tcp.header_len() as usize);
+
+                        // set data offset to 0 to trigger an error
+                        data[base_len + 12] = data[base_len + 12] & 0b0000_1111;
+
+                        let err = err::tcp::HeaderError::DataOffsetTooSmall { data_offset: 0 };
+                        assert_test_result(
+                            &test,
+                            &[],
+                            &data,
+                            None,
+                            Some((SliceError::Tcp(err.clone()), Layer::TcpHeader)),
+                        );
+                    }
+                }
+            }
+
+            // icmpv4
+            {
+                let icmpv4 =
+                    Icmpv4Header::new(Icmpv4Type::EchoReply(IcmpEchoHeader { id: 1, seq: 2 }));
+                let mut test = base.clone();
+                test.net = Some({
+                    let mut ip = match ip {
+                        NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
+                        NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
+                    };
+                    ip.set_next_headers(ip_number::ICMP);
+                    ip.into()
+                });
+                test.transport = Some(TransportHeader::Icmpv4(icmpv4.clone()));
+                test.set_payload_len(0);
+
+                // ok decode
+                from_x_slice_assert_ok(&test);
+
+                // length error
+                if false == test.is_ip_payload_fragmented() {
+                    for len in 0..icmpv4.header_len() {
+                        // set payload length
+                        let mut test = test.clone();
+                        test.set_payload_le_from_ip_on(len as isize);
+
+                        let data = test.to_vec(&[]);
+                        let base_len = test.len(&[]) - icmpv4.header_len();
+
+                        let err = LenError {
+                            required_len: icmpv4.header_len(),
+                            len,
+                            len_source: match test.net.as_ref().unwrap() {
+                                NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
+                                NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
+                            },
+                            layer: Layer::Icmpv4,
+                            layer_start_offset: base_len,
+                        };
+                        assert_test_result(
+                            &test,
+                            &[],
+                            &data[..base_len + len],
+                            None,
+                            Some((SliceError::Len(err.clone()), Layer::Icmpv4)),
+                        );
+                    }
+                }
+            }
+
+            // icmpv6
+            {
+                let icmpv6 =
+                    Icmpv6Header::new(Icmpv6Type::EchoReply(IcmpEchoHeader { id: 1, seq: 2 }));
+                let mut test = base.clone();
+                test.net = Some({
+                    let mut ip = match ip {
+                        NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
+                        NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
+                    };
+                    ip.set_next_headers(ip_number::IPV6_ICMP);
+                    ip.into()
+                });
+                test.transport = Some(TransportHeader::Icmpv6(icmpv6.clone()));
+                test.set_payload_len(0);
+
+                // ok decode
+                from_x_slice_assert_ok(&test);
+
+                // length error
+                if false == test.is_ip_payload_fragmented() {
+                    for len in 0..icmpv6.header_len() {
+                        // set payload length
+                        let mut test = test.clone();
+                        test.set_payload_le_from_ip_on(len as isize);
+
+                        let data = test.to_vec(&[]);
+                        let base_len = test.len(&[]) - icmpv6.header_len();
+
+                        let err = LenError {
+                            required_len: icmpv6.header_len(),
+                            len,
+                            len_source: match test.net.as_ref().unwrap() {
+                                NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
+                                NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
+                            },
+                            layer: Layer::Icmpv6,
+                            layer_start_offset: base_len,
+                        };
+                        assert_test_result(
+                            &test,
+                            &[],
+                            &data[..base_len + len],
+                            None,
+                            Some((SliceError::Len(err.clone()), Layer::Icmpv6)),
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    fn from_x_slice_assert_ok(test_base: &TestPacket) {
+        // setup payload
+        let payload = [1, 2, 3, 4];
+
+        // set length fields in ip headers
+        let test = {
+            let mut test = test_base.clone();
+            test.set_payload_len(payload.len());
+            test
+        };
+
+        // write data
+        let data = test.to_vec(&payload);
+        assert_test_result(&test, &payload, &data, None, None);
+    }
+
+    /// Check that the given output & errors (if present) are generated based on the given
+    /// input.
+    fn assert_test_result(
+        test: &TestPacket,
+        expected_payload: &[u8],
+        data: &[u8],
+        expected_ip_err: Option<err::ip::LaxHeaderSliceError>,
+        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() {
+                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);
+                    }
+                }
+                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);
+                    }
+                }
+                None => {
+                    assert_eq!(None, actual.vlan);
+                }
+            }
+        }
+
+        fn compare_ip_header_only(test: &TestPacket, actual: &LaxPacketHeaders) {
+            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()),
+                    }
+                }),
+                actual.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()),
+                    }
+                })
+            );
+        }
+
+        fn compare_transport(
+            test: &TestPacket,
+            is_fragmented: bool,
+            expected_payload: &[u8],
+            actual: &LaxPacketHeaders,
+        ) {
+            if is_fragmented {
+                assert_eq!(actual.transport, None);
+            } else {
+                use TransportHeader as H;
+                match &actual.transport {
+                    Some(H::Icmpv4(icmpv4)) => {
+                        assert_eq!(&test.transport, &Some(H::Icmpv4(icmpv4.clone())));
+                        assert_eq!(
+                            actual.payload,
+                            LaxPayloadSlice::Icmpv4 {
+                                payload: expected_payload,
+                                incomplete: false
+                            }
+                        );
+                    }
+                    Some(H::Icmpv6(icmpv6)) => {
+                        assert_eq!(&test.transport, &Some(H::Icmpv6(icmpv6.clone())));
+                        assert_eq!(
+                            actual.payload,
+                            LaxPayloadSlice::Icmpv6 {
+                                payload: expected_payload,
+                                incomplete: false
+                            }
+                        );
+                    }
+                    Some(H::Udp(s)) => {
+                        assert_eq!(&test.transport, &Some(H::Udp(s.clone())));
+                        assert_eq!(
+                            actual.payload,
+                            LaxPayloadSlice::Udp {
+                                payload: expected_payload,
+                                incomplete: false
+                            }
+                        );
+                    }
+                    Some(H::Tcp(s)) => {
+                        assert_eq!(&test.transport, &Some(H::Tcp(s.clone())));
+                        assert_eq!(
+                            actual.payload,
+                            LaxPayloadSlice::Tcp {
+                                payload: expected_payload,
+                                incomplete: false
+                            }
+                        );
+                    }
+                    None => {
+                        assert_eq!(&test.transport, &None);
+                    }
+                }
+            }
+        }
+
+        // from_ethernet_slice
+        if test.link.is_some() {
+            if data.len() < Ethernet2Header::LEN {
+                assert_eq!(
+                    LenError {
+                        required_len: Ethernet2Header::LEN,
+                        len: data.len(),
+                        len_source: LenSource::Slice,
+                        layer: Layer::Ethernet2Header,
+                        layer_start_offset: 0
+                    },
+                    LaxPacketHeaders::from_ethernet(&data).unwrap_err()
+                );
+            } else {
+                let actual = LaxPacketHeaders::from_ethernet(&data).unwrap();
+                assert_eq!(actual.stop_err, expected_stop_err);
+                match expected_stop_err.as_ref().map(|v| v.1) {
+                    None => {
+                        assert_eq!(test.link, actual.link);
+                        compare_vlan(test, data, &actual);
+                        assert_eq!(test.net, actual.net);
+                        compare_transport(
+                            test,
+                            test.is_ip_payload_fragmented(),
+                            expected_payload,
+                            &actual,
+                        );
+                    }
+                    Some(Layer::VlanHeader) => {
+                        assert_eq!(test.link, actual.link);
+                        compare_vlan(test, data, &actual);
+                        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) => {
+                        assert_eq!(test.link, actual.link);
+                        compare_vlan(test, data, &actual);
+                        assert_eq!(None, actual.net);
+                        assert_eq!(None, actual.transport);
+                        assert!(matches!(actual.payload, LaxPayloadSlice::Ether(_)));
+                    }
+                    Some(Layer::IpAuthHeader)
+                    | Some(Layer::Ipv6ExtHeader)
+                    | Some(Layer::Ipv6HopByHopHeader)
+                    | Some(Layer::Ipv6DestOptionsHeader)
+                    | Some(Layer::Ipv6RouteHeader)
+                    | Some(Layer::Ipv6FragHeader) => {
+                        assert_eq!(test.link, actual.link);
+                        compare_vlan(test, data, &actual);
+                        compare_ip_header_only(test, &actual);
+                        assert_eq!(None, actual.transport);
+                        assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_)));
+                    }
+                    Some(Layer::TcpHeader)
+                    | Some(Layer::UdpHeader)
+                    | Some(Layer::Icmpv4)
+                    | Some(Layer::Icmpv6) => {
+                        assert_eq!(test.link, actual.link);
+                        compare_vlan(test, data, &actual);
+                        assert_eq!(test.net, actual.net);
+                        assert_eq!(None, actual.transport);
+                        assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_)));
+                    }
+                    _ => unreachable!("error in an unexpected layer"),
+                }
+            }
+        }
+        // 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);
+                assert_eq!(actual.stop_err, expected_stop_err);
+                compare_vlan(test, data, &actual);
+                match expected_stop_err.as_ref().map(|v| v.1) {
+                    None => {
+                        assert_eq!(test.net, actual.net);
+                        compare_transport(
+                            test,
+                            test.is_ip_payload_fragmented(),
+                            expected_payload,
+                            &actual,
+                        );
+                    }
+                    Some(Layer::VlanHeader) => {
+                        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) => {
+                        assert_eq!(None, actual.net);
+                        assert_eq!(None, actual.transport);
+                        assert!(matches!(actual.payload, LaxPayloadSlice::Ether(_)));
+                    }
+                    Some(Layer::IpAuthHeader)
+                    | Some(Layer::Ipv6ExtHeader)
+                    | Some(Layer::Ipv6HopByHopHeader)
+                    | Some(Layer::Ipv6DestOptionsHeader)
+                    | Some(Layer::Ipv6RouteHeader)
+                    | Some(Layer::Ipv6FragHeader) => {
+                        compare_ip_header_only(test, &actual);
+                        assert_eq!(None, actual.transport);
+                        assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_)));
+                    }
+                    Some(Layer::TcpHeader)
+                    | Some(Layer::UdpHeader)
+                    | Some(Layer::Icmpv4)
+                    | Some(Layer::Icmpv6) => {
+                        assert_eq!(test.net, actual.net);
+                        assert_eq!(None, actual.transport);
+                        assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_)));
+                    }
+                    _ => unreachable!("error in an unexpected layer"),
+                }
+            }
+        }
+        // from_ether_type (ip at start)
+        if test.link.is_none() && test.vlan.is_none() {
+            if let Some(ip) = &test.net {
+                let ether_type = match ip {
+                    NetHeaders::Ipv4(_, _) => ether_type::IPV4,
+                    NetHeaders::Ipv6(_, _) => ether_type::IPV6,
+                };
+                let actual = LaxPacketHeaders::from_ether_type(ether_type, &data);
+                assert_eq!(actual.stop_err, expected_stop_err);
+                assert_eq!(None, actual.link);
+                assert_eq!(test.vlan, None);
+                match expected_stop_err.as_ref().map(|v| v.1) {
+                    None => {
+                        assert_eq!(test.net, actual.net);
+                        compare_transport(
+                            test,
+                            test.is_ip_payload_fragmented(),
+                            expected_payload,
+                            &actual,
+                        );
+                    }
+                    Some(Layer::Ipv6Header) | Some(Layer::Ipv4Header) | Some(Layer::IpHeader) => {
+                        assert_eq!(None, actual.net);
+                        assert_eq!(None, actual.transport);
+                        assert_eq!(
+                            LaxPayloadSlice::Ether(EtherPayloadSlice {
+                                ether_type,
+                                payload: data
+                            }),
+                            actual.payload
+                        );
+                    }
+                    Some(Layer::IpAuthHeader)
+                    | Some(Layer::Ipv6ExtHeader)
+                    | Some(Layer::Ipv6HopByHopHeader)
+                    | Some(Layer::Ipv6DestOptionsHeader)
+                    | Some(Layer::Ipv6RouteHeader)
+                    | Some(Layer::Ipv6FragHeader) => {
+                        compare_ip_header_only(test, &actual);
+                        assert_eq!(None, actual.transport);
+                        assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_)));
+                    }
+                    Some(Layer::TcpHeader)
+                    | Some(Layer::UdpHeader)
+                    | Some(Layer::Icmpv4)
+                    | Some(Layer::Icmpv6) => {
+                        assert_eq!(test.net, actual.net);
+                        assert_eq!(None, actual.transport);
+                        assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_)));
+                    }
+                    _ => unreachable!("error in an unexpected layer"),
+                }
+            }
+        }
+        // from_ip_slice
+        if test.link.is_none() && test.vlan.is_none() && test.net.is_some() {
+            if let Some(err) = expected_ip_err {
+                assert_eq!(err, LaxPacketHeaders::from_ip(&data).unwrap_err());
+            } else {
+                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);
+                match expected_stop_err.as_ref().map(|v| v.1) {
+                    None => {
+                        assert_eq!(test.net, actual.net);
+                        compare_transport(
+                            test,
+                            test.is_ip_payload_fragmented(),
+                            expected_payload,
+                            &actual,
+                        );
+                    }
+                    Some(Layer::IpAuthHeader)
+                    | Some(Layer::Ipv6ExtHeader)
+                    | Some(Layer::Ipv6HopByHopHeader)
+                    | Some(Layer::Ipv6DestOptionsHeader)
+                    | Some(Layer::Ipv6RouteHeader)
+                    | Some(Layer::Ipv6FragHeader) => {
+                        compare_ip_header_only(test, &actual);
+                        assert_eq!(None, actual.transport);
+                        assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_)));
+                    }
+                    Some(Layer::TcpHeader)
+                    | Some(Layer::UdpHeader)
+                    | Some(Layer::Icmpv4)
+                    | Some(Layer::Icmpv6) => {
+                        assert_eq!(test.net, actual.net);
+                        assert_eq!(None, actual.transport);
+                        assert!(matches!(actual.payload, LaxPayloadSlice::Ip(_)));
+                    }
+                    _ => unreachable!("error in an unexpected layer"),
+                }
+            }
+        }
+    }
+}
diff --git a/src/lax_payload_slice.rs b/src/lax_payload_slice.rs
new file mode 100644
index 0000000..704531b
--- /dev/null
+++ b/src/lax_payload_slice.rs
@@ -0,0 +1,166 @@
+use crate::*;
+
+/// Laxly parsed payload together with an identifier the type of content & the
+/// information if the payload is incomplete.
+#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
+pub enum LaxPayloadSlice<'a> {
+    /// Payload with it's type identified by an ether type number
+    /// (e.g. after an ethernet II or vlan header).
+    Ether(EtherPayloadSlice<'a>),
+    /// 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 },
+    /// 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 {
+        payload: &'a [u8],
+        /// 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 {
+        payload: &'a [u8],
+        /// True if the payload has been cut off.
+        incomplete: bool,
+    },
+}
+
+impl<'a> LaxPayloadSlice<'a> {
+    pub fn slice(&self) -> &'a [u8] {
+        match self {
+            LaxPayloadSlice::Ether(e) => e.payload,
+            LaxPayloadSlice::Ip(i) => i.payload,
+            LaxPayloadSlice::Udp {
+                payload,
+                incomplete: _,
+            } => payload,
+            LaxPayloadSlice::Tcp {
+                payload,
+                incomplete: _,
+            } => payload,
+            LaxPayloadSlice::Icmpv4 {
+                payload,
+                incomplete: _,
+            } => payload,
+            LaxPayloadSlice::Icmpv6 {
+                payload,
+                incomplete: _,
+            } => payload,
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        assert_eq!(
+            format!("Udp {{ payload: {:?}, incomplete: {} }}", &[0u8; 0], false),
+            format!(
+                "{:?}",
+                LaxPayloadSlice::Udp {
+                    payload: &[],
+                    incomplete: false
+                }
+            )
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash_ord() {
+        let s = LaxPayloadSlice::Udp {
+            payload: &[],
+            incomplete: false,
+        };
+        assert_eq!(s.clone(), s);
+
+        use std::collections::hash_map::DefaultHasher;
+        use std::hash::{Hash, Hasher};
+
+        let a_hash = {
+            let mut hasher = DefaultHasher::new();
+            s.hash(&mut hasher);
+            hasher.finish()
+        };
+        let b_hash = {
+            let mut hasher = DefaultHasher::new();
+            s.clone().hash(&mut hasher);
+            hasher.finish()
+        };
+        assert_eq!(a_hash, b_hash);
+
+        use std::cmp::Ordering;
+        assert_eq!(s.clone().cmp(&s), Ordering::Equal);
+        assert_eq!(s.clone().partial_cmp(&s), Some(Ordering::Equal));
+    }
+
+    #[test]
+    fn slice() {
+        let payload = [1, 2, 3, 4];
+
+        use LaxPayloadSlice::*;
+        assert_eq!(
+            Ether(EtherPayloadSlice {
+                ether_type: EtherType::IPV4,
+                payload: &payload
+            })
+            .slice(),
+            &payload
+        );
+        assert_eq!(
+            Ip(LaxIpPayloadSlice {
+                ip_number: IpNumber::IPV4,
+                fragmented: false,
+                len_source: LenSource::Slice,
+                payload: &payload,
+                incomplete: true,
+            })
+            .slice(),
+            &payload
+        );
+        assert_eq!(
+            Udp {
+                payload: &payload,
+                incomplete: false
+            }
+            .slice(),
+            &payload
+        );
+        assert_eq!(
+            Tcp {
+                payload: &payload,
+                incomplete: false
+            }
+            .slice(),
+            &payload
+        );
+        assert_eq!(
+            Icmpv4 {
+                payload: &payload,
+                incomplete: false
+            }
+            .slice(),
+            &payload
+        );
+        assert_eq!(
+            Icmpv6 {
+                payload: &payload,
+                incomplete: false
+            }
+            .slice(),
+            &payload
+        );
+    }
+}
diff --git a/src/lax_sliced_packet.rs b/src/lax_sliced_packet.rs
new file mode 100644
index 0000000..3c6364c
--- /dev/null
+++ b/src/lax_sliced_packet.rs
@@ -0,0 +1,1611 @@
+use crate::{err::Layer, *};
+
+/// Packet slice split into multiple slices containing
+/// the different headers & payload.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct LaxSlicedPacket<'a> {
+    /// Ethernet II header if present.
+    pub link: Option<LinkSlice<'a>>,
+
+    /// Single or double vlan headers if present.
+    pub vlan: Option<VlanSlice<'a>>,
+
+    /// IPv4 or IPv6 header, IP extension headers & payload if present.
+    pub net: Option<LaxNetSlice<'a>>,
+
+    /// TCP or UDP header & payload if present.
+    pub transport: Option<TransportSlice<'a>>,
+
+    /// Error that stopped the parsing and the layer on which the stop occurred.
+    pub stop_err: Option<(err::packet::SliceError, Layer)>,
+}
+
+impl<'a> LaxSlicedPacket<'a> {
+    /// Separates a network packet slice into different slices containing the
+    /// headers from the ethernet header downwards with lax length checks and
+    /// non-terminating errors.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    ///```
+    /// # use etherparse::{Ethernet2Header, PacketBuilder};
+    /// # let builder = PacketBuilder::
+    /// #    ethernet2([1,2,3,4,5,6],     //source mac
+    /// #               [7,8,9,10,11,12]) //destionation 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
+    /// # // payload of the udp packet
+    /// # let payload = [1,2,3,4,5,6,7,8];
+    /// # // get some memory to store the serialized data
+    /// # let mut packet = Vec::<u8>::with_capacity(
+    /// #     builder.size(payload.len())
+    /// # );
+    /// # builder.write(&mut packet, &payload).unwrap();
+    /// #
+    /// use etherparse::{ether_type, LaxSlicedPacket, LenSource};
+    ///
+    /// match LaxSlicedPacket::from_ethernet(&packet) {
+    ///     Err(value) => {
+    ///         // An error is returned in case the ethernet II header could
+    ///         // not be parsed (other errors are stored in the "stop_err" field)
+    ///         println!("Err {:?}", value)
+    ///     },
+    ///     Ok(value) => {
+    ///         if let Some((stop_err, error_layer)) = value.stop_err.as_ref() {
+    ///             // error was encountered after parsing the ethernet 2 header
+    ///             println!("Error on layer {}: {:?}", error_layer, stop_err);
+    ///         }
+    ///
+    ///         // parts that could be parsed without error
+    ///         println!("link: {:?}", value.link);
+    ///         println!("vlan: {:?}", value.vlan);
+    ///         println!("net: {:?}", value.net);
+    ///         println!("transport: {:?}", value.transport);
+    ///
+    ///         // net (ip) & transport (udp or tcp)
+    ///         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
+    ///             // if the slice length was used as a fallback value
+    ///             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!("transport: {:?}", value.transport);
+    ///     }
+    /// }
+    ///
+    /// ```
+    pub fn from_ethernet(slice: &'a [u8]) -> Result<LaxSlicedPacket<'a>, err::LenError> {
+        LaxSlicedPacketCursor::parse_from_ethernet2(slice)
+    }
+
+    /// Separates a network packet slice into different slices containing the headers using
+    /// the given `ether_type` number to identify the first header with lax length
+    /// checks and non-terminating errors.
+    ///
+    /// The result is returned as a [`LaxSlicedPacket`] struct. Currently supported
+    /// ether type numbers are:
+    ///
+    /// * `ether_type::IPV4`
+    /// * `ether_type::IPV6`
+    /// * `ether_type::VLAN_TAGGED_FRAME`
+    /// * `ether_type::PROVIDER_BRIDGING`
+    /// * `ether_type::VLAN_DOUBLE_TAGGED_FRAME`
+    ///
+    /// If an unsupported ether type is given the given slice will be set as payload
+    /// and all other fields will be set to `None`.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    ///```
+    /// # use etherparse::{Ethernet2Header, PacketBuilder};
+    /// # let builder = PacketBuilder::
+    /// #    ethernet2([1,2,3,4,5,6],     //source mac
+    /// #               [7,8,9,10,11,12]) //destionation 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
+    /// # // payload of the udp packet
+    /// # let payload = [1,2,3,4,5,6,7,8];
+    /// # // get some memory to store the serialized data
+    /// # let mut complete_packet = Vec::<u8>::with_capacity(
+    /// #     builder.size(payload.len())
+    /// # );
+    /// # builder.write(&mut complete_packet, &payload).unwrap();
+    /// #
+    /// # // skip ethernet 2 header so we can parse from there downwards
+    /// # let packet = &complete_packet[Ethernet2Header::LEN..];
+    /// #
+    /// use etherparse::{ether_type, LaxSlicedPacket};
+    ///
+    /// let packet = LaxSlicedPacket::from_ether_type(ether_type::IPV4, packet);
+    /// if let Some((stop_err, error_layer)) = packet.stop_err.as_ref() {
+    ///     // in case an error is encountered parsing is stopped
+    ///     println!("Error on layer {}: {:?}", error_layer, stop_err);
+    /// }
+    ///
+    /// // parts that could be parsed without error
+    /// println!("link: {:?}", packet.link);
+    /// println!("vlan: {:?}", packet.vlan);
+    /// println!("net: {:?}", packet.net);
+    /// println!("transport: {:?}", packet.transport);
+    ///
+    /// ```
+    pub fn from_ether_type(ether_type: EtherType, slice: &'a [u8]) -> LaxSlicedPacket<'a> {
+        LaxSlicedPacketCursor::parse_from_ether_type(ether_type, slice)
+    }
+
+    /// Separates a network packet slice into different slices containing
+    /// the headers from the ip header downwards with lax length checks
+    /// and will still return a result even if an error is encountered in
+    /// a layer (except IP).
+    ///
+    /// This function has two main differences to `SlicedPacket::from_ip`:
+    ///
+    /// * Errors encountered bellow the IpHeader will only stop the parsing and
+    ///   return an `Ok` 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.
+    /// * Length in the IP header & UDP headers are allowed to be inconsistent with the
+    ///   given slice length (e.g. data is missing from the slice). In this case it falls
+    ///   back to the length of slice. See [`LaxIpSlice::from_slice`] for a detailed
+    ///   description of when the slice length is used as a fallback.
+    ///
+    /// The result is returned as a [`SlicedPacket`] struct. This function
+    /// assumes the given data starts with an IPv4 or IPv6 header.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    ///```
+    /// # use etherparse::{PacketBuilder, IpSlice, LenSource};
+    /// # let builder = PacketBuilder::
+    /// #    ipv4([192,168,1,1], //source ip
+    /// #         [192,168,1,2], //destination ip
+    /// #         20)            //time to life
+    /// #    .udp(21,    //source port
+    /// #         1234); //desitnation port
+    /// #    //payload of the udp packet
+    /// #    let payload = [1,2,3,4,5,6,7,8];
+    /// #    //get some memory to store the serialized data
+    /// #    let mut packet = Vec::<u8>::with_capacity(
+    /// #                            builder.size(payload.len()));
+    /// #    builder.write(&mut packet, &payload).unwrap();
+    /// use etherparse::LaxSlicedPacket;
+    ///
+    /// match LaxSlicedPacket::from_ip(&packet) {
+    ///     Err(value) => {
+    ///         // An error is returned in case the ip header could
+    ///         // not parsed (other errors are stored in the "stop_err" field)
+    ///         println!("Err {:?}", value)
+    ///     },
+    ///     Ok(value) => {
+    ///         if let Some((stop_err, error_layer)) = value.stop_err.as_ref() {
+    ///             // error is encountered after the ip header (stops parsing)
+    ///             println!("Error on layer {}: {:?}", error_layer, stop_err);
+    ///         }
+    ///
+    ///         // link & vlan fields are empty when parsing from ip downwards
+    ///         assert_eq!(None, value.link);
+    ///         assert_eq!(None, value.vlan);
+    ///
+    ///         // net (ip) & transport (udp or tcp)
+    ///         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
+    ///             // if the slice length was used as a fallback value
+    ///             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!("transport: {:?}", value.transport);
+    ///     }
+    /// }
+    /// ```
+    pub fn from_ip(slice: &'a [u8]) -> Result<LaxSlicedPacket<'a>, err::ip::LaxHeaderSliceError> {
+        LaxSlicedPacketCursor::parse_from_ip(slice)
+    }
+
+    /// Returns the last ether payload of the packet (if one is present).
+    ///
+    /// If VLAN header is present the payload after the most inner VLAN
+    /// header is returned and if there is no VLAN header is present in the
+    /// link field is returned.
+    pub fn ether_payload(&self) -> Option<EtherPayloadSlice<'a>> {
+        if let Some(vlan) = self.vlan.as_ref() {
+            match vlan {
+                VlanSlice::SingleVlan(s) => Some(s.payload()),
+                VlanSlice::DoubleVlan(s) => Some(s.payload()),
+            }
+        } else if let Some(link) = self.link.as_ref() {
+            match link {
+                LinkSlice::Ethernet2(e) => Some(e.payload()),
+                LinkSlice::LinuxSll(e) => match e.protocol_type() {
+                    LinuxSllProtocolType::EtherType(_)
+                    | LinuxSllProtocolType::LinuxNonstandardEtherType(_) => {
+                        Some(EtherPayloadSlice::try_from(e.payload()).ok()?)
+                    }
+                    _ => None,
+                },
+                LinkSlice::EtherPayload(e) => Some(e.clone()),
+                LinkSlice::LinuxSllPayload(e) => match e.protocol_type {
+                    LinuxSllProtocolType::EtherType(_)
+                    | LinuxSllProtocolType::LinuxNonstandardEtherType(_) => {
+                        Some(EtherPayloadSlice::try_from(e.clone()).ok()?)
+                    }
+                    _ => None,
+                },
+            }
+        } else {
+            None
+        }
+    }
+
+    /// Return the IP payload after the the IP header and the IP extension
+    /// headers (if one is present).
+    pub fn ip_payload(&self) -> Option<&LaxIpPayloadSlice<'a>> {
+        if let Some(net) = self.net.as_ref() {
+            use LaxNetSlice::*;
+            match net {
+                Ipv4(v) => Some(v.payload()),
+                Ipv6(v) => Some(v.payload()),
+            }
+        } else {
+            None
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::err::{packet::SliceError, LenError};
+    use crate::test_packet::TestPacket;
+
+    const VLAN_ETHER_TYPES: [EtherType; 3] = [
+        ether_type::VLAN_TAGGED_FRAME,
+        ether_type::PROVIDER_BRIDGING,
+        ether_type::VLAN_DOUBLE_TAGGED_FRAME,
+    ];
+
+    #[test]
+    fn clone_eq() {
+        let header = LaxSlicedPacket {
+            link: None,
+            vlan: None,
+            net: None,
+            transport: None,
+            stop_err: None,
+        };
+        assert_eq!(header.clone(), header);
+    }
+
+    #[test]
+    fn debug() {
+        use alloc::format;
+        let header = LaxSlicedPacket {
+            link: None,
+            vlan: None,
+            net: None,
+            transport: None,
+            stop_err: None,
+        };
+        assert_eq!(
+            format!("{:?}", header),
+            format!(
+                "LaxSlicedPacket {{ link: {:?}, vlan: {:?}, net: {:?}, transport: {:?}, stop_err: {:?} }}",
+                header.link, header.vlan, header.net, header.transport, header.stop_err
+            )
+        );
+    }
+
+    #[test]
+    fn ether_payload() {
+        use alloc::vec::*;
+
+        // no content
+        assert_eq!(
+            LaxSlicedPacket {
+                link: None,
+                vlan: None,
+                net: None,
+                transport: None,
+                stop_err: None
+            }
+            .ether_payload(),
+            None
+        );
+
+        // only ethernet header II
+        {
+            let payload = [1, 2, 3, 4];
+            let mut buf = Vec::with_capacity(Ethernet2Header::LEN + 4);
+            buf.extend_from_slice(
+                &Ethernet2Header {
+                    ether_type: EtherType::WAKE_ON_LAN,
+                    ..Default::default()
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(&payload);
+            assert_eq!(
+                LaxSlicedPacket::from_ethernet(&buf)
+                    .unwrap()
+                    .ether_payload(),
+                Some(EtherPayloadSlice {
+                    ether_type: EtherType::WAKE_ON_LAN,
+                    payload: &payload
+                })
+            );
+        }
+
+        // ether type payload
+        {
+            let payload = [1, 2, 3, 4];
+            assert_eq!(
+                LaxSlicedPacket {
+                    link: Some(LinkSlice::EtherPayload(EtherPayloadSlice {
+                        ether_type: EtherType::WAKE_ON_LAN,
+                        payload: &payload
+                    })),
+                    vlan: None,
+                    net: None,
+                    transport: None,
+                    stop_err: None,
+                }
+                .ether_payload(),
+                Some(EtherPayloadSlice {
+                    ether_type: EtherType::WAKE_ON_LAN,
+                    payload: &payload
+                })
+            );
+        }
+
+        // single vlan header
+        {
+            let payload = [1, 2, 3, 4];
+            let mut buf = Vec::with_capacity(Ethernet2Header::LEN + SingleVlanHeader::LEN + 4);
+            buf.extend_from_slice(
+                &Ethernet2Header {
+                    ether_type: EtherType::VLAN_TAGGED_FRAME,
+                    ..Default::default()
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(
+                &SingleVlanHeader {
+                    ether_type: EtherType::WAKE_ON_LAN,
+                    ..Default::default()
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(&payload);
+            assert_eq!(
+                LaxSlicedPacket::from_ethernet(&buf)
+                    .unwrap()
+                    .ether_payload(),
+                Some(EtherPayloadSlice {
+                    ether_type: EtherType::WAKE_ON_LAN,
+                    payload: &payload
+                })
+            );
+        }
+
+        // double vlan header
+        {
+            let payload = [1, 2, 3, 4];
+            let mut buf = Vec::with_capacity(Ethernet2Header::LEN + SingleVlanHeader::LEN * 2 + 4);
+            buf.extend_from_slice(
+                &Ethernet2Header {
+                    ether_type: EtherType::VLAN_DOUBLE_TAGGED_FRAME,
+                    ..Default::default()
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(
+                &SingleVlanHeader {
+                    ether_type: EtherType::VLAN_TAGGED_FRAME,
+                    ..Default::default()
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(
+                &SingleVlanHeader {
+                    ether_type: EtherType::WAKE_ON_LAN,
+                    ..Default::default()
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(&payload);
+            assert_eq!(
+                LaxSlicedPacket::from_ethernet(&buf)
+                    .unwrap()
+                    .ether_payload(),
+                Some(EtherPayloadSlice {
+                    ether_type: EtherType::WAKE_ON_LAN,
+                    payload: &payload
+                })
+            );
+        }
+    }
+
+    #[test]
+    fn ip_payload() {
+        use alloc::vec::*;
+
+        // no content
+        assert_eq!(
+            LaxSlicedPacket {
+                link: None,
+                vlan: None,
+                net: None,
+                transport: None,
+                stop_err: None,
+            }
+            .ip_payload(),
+            None
+        );
+
+        // ipv4
+        {
+            let payload = [1, 2, 3, 4];
+            let mut buf = Vec::with_capacity(Ipv4Header::MIN_LEN + 4);
+            buf.extend_from_slice(
+                &Ipv4Header {
+                    protocol: IpNumber::ARIS,
+                    total_len: Ipv4Header::MIN_LEN_U16 + 4,
+                    ..Default::default()
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(&payload);
+            assert_eq!(
+                LaxSlicedPacket::from_ip(&buf).unwrap().ip_payload(),
+                Some(&LaxIpPayloadSlice {
+                    payload: &payload,
+                    ip_number: IpNumber::ARIS,
+                    fragmented: false,
+                    len_source: LenSource::Ipv4HeaderTotalLen,
+                    incomplete: false,
+                })
+            );
+        }
+
+        // ipv6
+        {
+            let payload = [1, 2, 3, 4];
+            let mut buf = Vec::with_capacity(Ipv6Header::LEN + 4);
+            buf.extend_from_slice(
+                &Ipv6Header {
+                    payload_length: 4,
+                    next_header: IpNumber::ARGUS,
+                    ..Default::default()
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(&payload);
+            assert_eq!(
+                LaxSlicedPacket::from_ip(&buf).unwrap().ip_payload(),
+                Some(&LaxIpPayloadSlice {
+                    payload: &payload,
+                    ip_number: IpNumber::ARGUS,
+                    fragmented: false,
+                    len_source: LenSource::Ipv6HeaderPayloadLen,
+                    incomplete: false,
+                })
+            );
+        }
+    }
+
+    #[test]
+    fn from_x_slice() {
+        // no eth
+        from_x_slice_vlan_variants(&TestPacket {
+            link: None,
+            vlan: None,
+            net: None,
+            transport: None,
+        });
+
+        // eth
+        {
+            let eth = Ethernet2Header {
+                source: [1, 2, 3, 4, 5, 6],
+                destination: [1, 2, 3, 4, 5, 6],
+                ether_type: 0.into(),
+            };
+            let test = TestPacket {
+                link: Some(LinkHeader::Ethernet2(eth.clone())),
+                vlan: None,
+                net: None,
+                transport: None,
+            };
+
+            // ok ethernet header (with unknown next)
+            from_x_slice_vlan_variants(&test);
+
+            // eth len error
+            {
+                let data = test.to_vec(&[]);
+                for len in 0..data.len() {
+                    assert_test_result(&test, &[], &data[..len], None, None);
+                }
+            }
+        }
+
+        // unknown ether_type
+        {
+            let payload = [1, 2, 3, 4];
+            let actual = LaxSlicedPacket::from_ether_type(0.into(), &payload);
+            assert_eq!(
+                actual.link,
+                Some(LinkSlice::EtherPayload(EtherPayloadSlice {
+                    ether_type: 0.into(),
+                    payload: &payload
+                }))
+            );
+            assert_eq!(None, actual.vlan);
+            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);
+
+        // single vlan header
+        {
+            let single = SingleVlanHeader {
+                pcp: 1.try_into().unwrap(),
+                drop_eligible_indicator: false,
+                vlan_id: 2.try_into().unwrap(),
+                ether_type: 3.into(),
+            };
+
+            for vlan_ether_type in VLAN_ETHER_TYPES {
+                let mut test = base.clone();
+                test.set_ether_type(vlan_ether_type);
+                test.vlan = Some(VlanHeader::Single(single.clone()));
+
+                // ok vlan header
+                from_x_slice_ip_variants(&test);
+
+                // len error
+                {
+                    let data = test.to_vec(&[]);
+                    for len in 0..single.header_len() {
+                        let base_len = test.len(&[]) - single.header_len();
+
+                        let err = LenError {
+                            required_len: single.header_len(),
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: Layer::VlanHeader,
+                            layer_start_offset: base_len,
+                        };
+                        assert_test_result(
+                            &test,
+                            &[],
+                            &data[..base_len + len],
+                            None,
+                            Some((SliceError::Len(err.clone()), Layer::VlanHeader)),
+                        );
+                    }
+                }
+            }
+        }
+
+        // double vlan header
+        for outer_vlan_ether_type in VLAN_ETHER_TYPES {
+            for inner_vlan_ether_type in VLAN_ETHER_TYPES {
+                let double = DoubleVlanHeader {
+                    outer: SingleVlanHeader {
+                        pcp: 1.try_into().unwrap(),
+                        drop_eligible_indicator: false,
+                        vlan_id: 2.try_into().unwrap(),
+                        ether_type: inner_vlan_ether_type,
+                    },
+                    inner: SingleVlanHeader {
+                        pcp: 1.try_into().unwrap(),
+                        drop_eligible_indicator: false,
+                        vlan_id: 2.try_into().unwrap(),
+                        ether_type: 3.into(),
+                    },
+                };
+                let mut test = base.clone();
+                test.set_ether_type(outer_vlan_ether_type);
+                test.vlan = Some(VlanHeader::Double(double.clone()));
+
+                // ok double vlan header
+                from_x_slice_ip_variants(&test);
+
+                // len error
+                {
+                    let data = test.to_vec(&[]);
+                    for len in 0..SingleVlanHeader::LEN {
+                        let base_len = test.len(&[]) - SingleVlanHeader::LEN;
+
+                        let err = LenError {
+                            required_len: SingleVlanHeader::LEN,
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: Layer::VlanHeader,
+                            layer_start_offset: base_len,
+                        };
+                        assert_test_result(
+                            &test,
+                            &[],
+                            &data[..base_len + len],
+                            None,
+                            Some((SliceError::Len(err.clone()), Layer::VlanHeader)),
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    fn from_x_slice_ip_variants(base: &TestPacket) {
+        // none
+        from_x_slice_transport_variants(base);
+
+        // ipv4
+        for fragmented in [false, true] {
+            let ipv4 = {
+                let mut ipv4 =
+                    Ipv4Header::new(0, 1, 2.into(), [3, 4, 5, 6], [7, 8, 9, 10]).unwrap();
+                ipv4.more_fragments = fragmented;
+                ipv4
+            };
+
+            {
+                let mut test = base.clone();
+                test.set_ether_type(ether_type::IPV4);
+                test.net = Some(NetHeaders::Ipv4(ipv4.clone(), Default::default()));
+                test.set_payload_len(0);
+
+                // ok ipv4
+                from_x_slice_transport_variants(&test);
+
+                // ipv4 len error
+                {
+                    let data = test.to_vec(&[]);
+                    for len in 0..ipv4.header_len() {
+                        let base_len = test.len(&[]) - ipv4.header_len();
+
+                        let err = LenError {
+                            required_len: if len < 1 { 1 } else { ipv4.header_len() },
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: if len < 1 {
+                                Layer::IpHeader
+                            } else {
+                                Layer::Ipv4Header
+                            },
+                            layer_start_offset: base_len,
+                        };
+
+                        assert_test_result(
+                            &test,
+                            &[],
+                            &data[..base_len + len],
+                            Some(err::ip::LaxHeaderSliceError::Len(err.clone())),
+                            Some((SliceError::Len(err.clone()), Layer::IpHeader)),
+                        );
+                    }
+                }
+
+                // ipv4 content error (ihl length too small)
+                {
+                    use err::ip::HeaderError::*;
+
+                    let mut data = test.to_vec(&[]);
+                    let ipv4_offset = data.len() - ipv4.header_len();
+
+                    // set the ihl to 0 to trigger a content error
+                    data[ipv4_offset] = 0b1111_0000 & data[ipv4_offset];
+
+                    assert_test_result(
+                        &test,
+                        &[],
+                        &data,
+                        Some(err::ip::LaxHeaderSliceError::Content(
+                            Ipv4HeaderLengthSmallerThanHeader { ihl: 0 },
+                        )),
+                        Some((
+                            SliceError::Ip(Ipv4HeaderLengthSmallerThanHeader { ihl: 0 }),
+                            Layer::IpHeader,
+                        )),
+                    );
+                }
+
+                // ipv 4total length too small (does not change the output)
+                {
+                    let mut data = test.to_vec(&[]);
+                    let ipv4_offset = data.len() - ipv4.header_len();
+
+                    // set the total length to 0 to trigger a content error
+                    data[ipv4_offset + 2] = 0;
+                    data[ipv4_offset + 3] = 0;
+
+                    let mut mod_test = test.clone();
+                    mod_test.net = Some({
+                        let (h, e) = test.net.as_ref().map(|v| v.ipv4_ref()).flatten().unwrap();
+                        let mut ipv4 = h.clone();
+                        ipv4.total_len = 0;
+                        NetHeaders::Ipv4(ipv4, e.clone())
+                    });
+
+                    assert_test_result(&mod_test, &[], &data, None, None);
+                }
+            }
+
+            // ipv4 extension content error
+            {
+                let auth = IpAuthHeader::new(0.into(), 1, 2, &[]).unwrap();
+
+                let mut test = base.clone();
+                test.set_ether_type(ether_type::IPV4);
+                test.net = Some(NetHeaders::Ipv4(
+                    {
+                        let mut ipv4 = ipv4.clone();
+                        ipv4.protocol = ip_number::AUTH;
+                        ipv4
+                    },
+                    Ipv4Extensions {
+                        auth: Some(auth.clone()),
+                    },
+                ));
+                test.set_payload_len(0);
+
+                // ok ipv4 & extension
+                from_x_slice_transport_variants(&test);
+
+                // ipv4 extension len error
+                for len in 0..auth.header_len() {
+                    // set payload length
+                    let mut test = test.clone();
+                    test.set_payload_le_from_ip_on(
+                        -1 * (auth.header_len() as isize) + (len as isize),
+                    );
+
+                    let data = test.to_vec(&[]);
+                    let base_len = test.len(&[]) - auth.header_len();
+
+                    let err = LenError {
+                        required_len: auth.header_len(),
+                        len,
+                        len_source: LenSource::Ipv4HeaderTotalLen,
+                        layer: Layer::IpAuthHeader,
+                        layer_start_offset: base_len,
+                    };
+
+                    assert_test_result(
+                        &test,
+                        &[],
+                        &data,
+                        None,
+                        Some((SliceError::Len(err.clone()), Layer::IpAuthHeader)),
+                    );
+                }
+
+                // ipv4 extension content error
+                {
+                    let mut data = test.to_vec(&[]);
+                    let auth_offset = data.len() - auth.header_len();
+
+                    // set the icv len too smaller then allowed
+                    data[auth_offset + 1] = 0;
+
+                    // expect an error
+                    assert_test_result(
+                        &test,
+                        &[],
+                        &data,
+                        None,
+                        Some((
+                            SliceError::Ipv4Exts(err::ip_auth::HeaderError::ZeroPayloadLen),
+                            Layer::IpAuthHeader,
+                        )),
+                    );
+                }
+            }
+        }
+
+        // ipv6
+        {
+            let ipv6 = Ipv6Header {
+                traffic_class: 0,
+                flow_label: 1.try_into().unwrap(),
+                payload_length: 2,
+                next_header: 3.into(),
+                hop_limit: 4,
+                source: [0; 16],
+                destination: [0; 16],
+            };
+
+            // ipv6 header only
+            {
+                let mut test = base.clone();
+                test.set_ether_type(ether_type::IPV6);
+                test.net = Some(NetHeaders::Ipv6(ipv6.clone(), Default::default()));
+                test.set_payload_len(0);
+
+                // ok ipv6
+                from_x_slice_transport_variants(&test);
+
+                // header len ipv6
+                {
+                    let data = test.to_vec(&[]);
+                    for len in 0..ipv6.header_len() {
+                        let base_len = test.len(&[]) - ipv6.header_len();
+
+                        let err = err::LenError {
+                            required_len: if len < 1 { 1 } else { ipv6.header_len() },
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: if len < 1 {
+                                Layer::IpHeader
+                            } else {
+                                Layer::Ipv6Header
+                            },
+                            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 < 1 {
+                                        let mut err = err.clone();
+                                        err.required_len = 1;
+                                        err.layer = Layer::IpHeader;
+                                        err
+                                    } else {
+                                        err.clone()
+                                    }
+                                }),
+                                Layer::IpHeader,
+                            )),
+                        );
+                    }
+                }
+
+                // content error ipv6
+                {
+                    use err::ip::{HeaderError::*, LaxHeaderSliceError::Content};
+
+                    let mut data = test.to_vec(&[]);
+
+                    // inject an invalid ip version
+                    let base_len = data.len() - ipv6.header_len();
+                    data[base_len] = data[base_len] & 0b0000_1111;
+
+                    assert_test_result(
+                        &test,
+                        &[],
+                        &data,
+                        Some(Content(UnsupportedIpVersion { version_number: 0 })),
+                        Some((
+                            SliceError::Ip(UnsupportedIpVersion { version_number: 0 }),
+                            Layer::IpHeader,
+                        )),
+                    );
+                }
+            }
+
+            // ipv6 + extension
+            for fragment in [false, true] {
+                let auth = IpAuthHeader::new(ip_number::GGP, 1, 2, &[]).unwrap();
+                let frag = Ipv6FragmentHeader {
+                    next_header: ip_number::AUTH,
+                    fragment_offset: 0.try_into().unwrap(),
+                    more_fragments: fragment,
+                    identification: 3,
+                };
+
+                let mut test = base.clone();
+                test.set_ether_type(ether_type::IPV6);
+                test.net = Some(NetHeaders::Ipv6(
+                    {
+                        let mut ipv6 = ipv6.clone();
+                        ipv6.next_header = ip_number::IPV6_FRAG;
+                        ipv6
+                    },
+                    {
+                        let mut exts: Ipv6Extensions = Default::default();
+                        exts.fragment = Some(frag.clone());
+                        exts.auth = Some(auth.clone());
+                        exts
+                    },
+                ));
+                test.set_payload_len(0);
+
+                // ok ipv6 & extensions
+                from_x_slice_transport_variants(&test);
+
+                // ipv6 extension len error
+                for len in 0..auth.header_len() {
+                    // set payload length
+                    let mut test = test.clone();
+                    test.set_payload_le_from_ip_on(
+                        -1 * (auth.header_len() as isize) + (len as isize),
+                    );
+
+                    let data = test.to_vec(&[]);
+                    let base_len = test.len(&[]) - auth.header_len();
+
+                    let err = LenError {
+                        required_len: auth.header_len(),
+                        len,
+                        len_source: LenSource::Ipv6HeaderPayloadLen,
+                        layer: Layer::IpAuthHeader,
+                        layer_start_offset: base_len,
+                    };
+                    assert_test_result(
+                        &test,
+                        &[],
+                        &data[..base_len + len],
+                        None,
+                        Some((SliceError::Len(err.clone()), Layer::IpAuthHeader)),
+                    );
+                }
+
+                // ipv6 extension content error (auth)
+                {
+                    let mut data = test.to_vec(&[]);
+                    let auth_offset = data.len() - auth.header_len();
+                    // set the icv len too smaller then allowed
+                    data[auth_offset + 1] = 0;
+
+                    assert_test_result(
+                        &test,
+                        &[],
+                        &data,
+                        None,
+                        Some((
+                            SliceError::Ipv6Exts(err::ipv6_exts::HeaderError::IpAuth(
+                                err::ip_auth::HeaderError::ZeroPayloadLen,
+                            )),
+                            Layer::IpAuthHeader,
+                        )),
+                    );
+                }
+
+                // ipv6 extension content error (hop by hop not at start)
+                {
+                    let mut data = test.to_vec(&[]);
+                    let auth_offset = data.len() - auth.header_len();
+
+                    // set the next header to be a hop-by-hop header to trigger a "not at start error"
+                    data[auth_offset] = 0;
+
+                    assert_test_result(
+                        &test,
+                        &[],
+                        &data,
+                        None,
+                        Some((
+                            SliceError::Ipv6Exts(err::ipv6_exts::HeaderError::HopByHopNotAtStart),
+                            Layer::Ipv6HopByHopHeader,
+                        )),
+                    );
+                }
+            }
+        }
+    }
+
+    fn from_x_slice_transport_variants(base: &TestPacket) {
+        // none
+        from_x_slice_assert_ok(base);
+
+        // transport can only be set if ip is present
+        if let Some(ip) = &base.net {
+            // udp
+            {
+                let udp = UdpHeader {
+                    source_port: 1,
+                    destination_port: 2,
+                    length: 3,
+                    checksum: 4,
+                };
+                let mut test = base.clone();
+                test.net = Some({
+                    let mut ip = match ip {
+                        NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
+                        NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
+                    };
+                    ip.set_next_headers(ip_number::UDP);
+                    ip.into()
+                });
+                test.transport = Some(TransportHeader::Udp(udp.clone()));
+                test.set_payload_len(0);
+
+                // ok decode
+                from_x_slice_assert_ok(&test);
+
+                // length error
+                if false == test.is_ip_payload_fragmented() {
+                    for len in 0..udp.header_len() {
+                        // build new test packet
+                        let mut test = test.clone();
+
+                        // set payload length
+                        test.set_payload_le_from_ip_on(len as isize);
+
+                        // generate data
+                        let data = test.to_vec(&[]);
+
+                        let base_len = test.len(&[]) - udp.header_len();
+                        let err = LenError {
+                            required_len: udp.header_len(),
+                            len,
+                            len_source: match test.net.as_ref().unwrap() {
+                                NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
+                                NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
+                            },
+                            layer: Layer::UdpHeader,
+                            layer_start_offset: base_len,
+                        };
+                        assert_test_result(
+                            &test,
+                            &[],
+                            &data[..base_len + len],
+                            None,
+                            Some((SliceError::Len(err.clone()), Layer::UdpHeader)),
+                        );
+                    }
+                }
+            }
+
+            // tcp
+            {
+                let tcp = TcpHeader::new(1, 2, 3, 4);
+                let mut test = base.clone();
+                test.net = Some({
+                    let mut ip = match ip {
+                        NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
+                        NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
+                    };
+                    ip.set_next_headers(ip_number::TCP);
+                    ip.into()
+                });
+                test.transport = Some(TransportHeader::Tcp(tcp.clone()));
+                test.set_payload_len(0);
+
+                // ok decode
+                from_x_slice_assert_ok(&test);
+
+                // error can only occur if ip does not fragment the packet
+                if false == test.is_ip_payload_fragmented() {
+                    // length error
+                    {
+                        for len in 0..(tcp.header_len() as usize) {
+                            // set payload length
+                            let mut test = test.clone();
+                            test.set_payload_le_from_ip_on(len as isize);
+
+                            let data = test.to_vec(&[]);
+                            let base_len = test.len(&[]) - (tcp.header_len() as usize);
+
+                            let err = LenError {
+                                required_len: tcp.header_len() as usize,
+                                len,
+                                len_source: match test.net.as_ref().unwrap() {
+                                    NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
+                                    NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
+                                },
+                                layer: Layer::TcpHeader,
+                                layer_start_offset: base_len,
+                            };
+                            assert_test_result(
+                                &test,
+                                &[],
+                                &data[..base_len + len],
+                                None,
+                                Some((SliceError::Len(err.clone()), Layer::TcpHeader)),
+                            );
+                        }
+                    }
+
+                    // content error
+                    {
+                        let mut data = test.to_vec(&[]);
+                        let base_len = test.len(&[]) - (tcp.header_len() as usize);
+
+                        // set data offset to 0 to trigger an error
+                        data[base_len + 12] = data[base_len + 12] & 0b0000_1111;
+
+                        let err = err::tcp::HeaderError::DataOffsetTooSmall { data_offset: 0 };
+                        assert_test_result(
+                            &test,
+                            &[],
+                            &data,
+                            None,
+                            Some((SliceError::Tcp(err.clone()), Layer::TcpHeader)),
+                        );
+                    }
+                }
+            }
+
+            // icmpv4
+            {
+                let icmpv4 =
+                    Icmpv4Header::new(Icmpv4Type::EchoReply(IcmpEchoHeader { id: 1, seq: 2 }));
+                let mut test = base.clone();
+                test.net = Some({
+                    let mut ip = match ip {
+                        NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
+                        NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
+                    };
+                    ip.set_next_headers(ip_number::ICMP);
+                    ip.into()
+                });
+                test.transport = Some(TransportHeader::Icmpv4(icmpv4.clone()));
+                test.set_payload_len(0);
+
+                // ok decode
+                from_x_slice_assert_ok(&test);
+
+                // length error
+                if false == test.is_ip_payload_fragmented() {
+                    for len in 0..icmpv4.header_len() {
+                        // set payload length
+                        let mut test = test.clone();
+                        test.set_payload_le_from_ip_on(len as isize);
+
+                        let data = test.to_vec(&[]);
+                        let base_len = test.len(&[]) - icmpv4.header_len();
+
+                        let err = LenError {
+                            required_len: icmpv4.header_len(),
+                            len,
+                            len_source: match test.net.as_ref().unwrap() {
+                                NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
+                                NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
+                            },
+                            layer: Layer::Icmpv4,
+                            layer_start_offset: base_len,
+                        };
+                        assert_test_result(
+                            &test,
+                            &[],
+                            &data[..base_len + len],
+                            None,
+                            Some((SliceError::Len(err.clone()), Layer::Icmpv4)),
+                        );
+                    }
+                }
+            }
+
+            // icmpv6
+            {
+                let icmpv6 =
+                    Icmpv6Header::new(Icmpv6Type::EchoReply(IcmpEchoHeader { id: 1, seq: 2 }));
+                let mut test = base.clone();
+                test.net = Some({
+                    let mut ip = match ip {
+                        NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
+                        NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
+                    };
+                    ip.set_next_headers(ip_number::IPV6_ICMP);
+                    ip.into()
+                });
+                test.transport = Some(TransportHeader::Icmpv6(icmpv6.clone()));
+                test.set_payload_len(0);
+
+                // ok decode
+                from_x_slice_assert_ok(&test);
+
+                // length error
+                if false == test.is_ip_payload_fragmented() {
+                    for len in 0..icmpv6.header_len() {
+                        // set payload length
+                        let mut test = test.clone();
+                        test.set_payload_le_from_ip_on(len as isize);
+
+                        let data = test.to_vec(&[]);
+                        let base_len = test.len(&[]) - icmpv6.header_len();
+
+                        let err = LenError {
+                            required_len: icmpv6.header_len(),
+                            len,
+                            len_source: match test.net.as_ref().unwrap() {
+                                NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
+                                NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
+                            },
+                            layer: Layer::Icmpv6,
+                            layer_start_offset: base_len,
+                        };
+                        assert_test_result(
+                            &test,
+                            &[],
+                            &data[..base_len + len],
+                            None,
+                            Some((SliceError::Len(err.clone()), Layer::Icmpv6)),
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    fn from_x_slice_assert_ok(test_base: &TestPacket) {
+        // setup payload
+        let payload = [1, 2, 3, 4];
+
+        // set length fields in ip headers
+        let test = {
+            let mut test = test_base.clone();
+            test.set_payload_len(payload.len());
+            test
+        };
+
+        // write data
+        let data = test.to_vec(&payload);
+        assert_test_result(&test, &payload, &data, None, None);
+    }
+
+    /// Check that the given output & errors (if present) are generated based on the given
+    /// input.
+    fn assert_test_result(
+        test: &TestPacket,
+        expected_payload: &[u8],
+        data: &[u8],
+        expected_ip_err: Option<err::ip::LaxHeaderSliceError>,
+        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() {
+                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);
+                    }
+                }
+                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);
+                    }
+                }
+                None => {
+                    assert_eq!(None, actual.vlan);
+                }
+            }
+        }
+
+        fn compare_ip(test: &TestPacket, actual: &LaxSlicedPacket) {
+            assert_eq!(
+                test.net,
+                actual.net.as_ref().map(|s| -> NetHeaders {
+                    match s {
+                        LaxNetSlice::Ipv4(ipv4) => NetHeaders::Ipv4(
+                            ipv4.header().to_header(),
+                            ipv4.extensions().to_header(),
+                        ),
+                        LaxNetSlice::Ipv6(ipv6) => NetHeaders::Ipv6(
+                            ipv6.header().to_header(),
+                            Ipv6Extensions::from_slice(
+                                ipv6.header().next_header(),
+                                ipv6.extensions().slice(),
+                            )
+                            .unwrap()
+                            .0,
+                        ),
+                    }
+                })
+            );
+        }
+
+        fn compare_ip_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()),
+                    }
+                }),
+                actual.net.as_ref().map(|s| -> NetHeaders {
+                    match s {
+                        LaxNetSlice::Ipv4(ipv4) => {
+                            NetHeaders::Ipv4(ipv4.header().to_header(), Default::default())
+                        }
+                        LaxNetSlice::Ipv6(ipv6) => {
+                            NetHeaders::Ipv6(ipv6.header().to_header(), Default::default())
+                        }
+                    }
+                })
+            );
+        }
+
+        fn compare_transport(
+            test: &TestPacket,
+            is_fragmented: bool,
+            expected_payload: &[u8],
+            actual: &LaxSlicedPacket,
+        ) {
+            if is_fragmented {
+                assert_eq!(actual.transport, None);
+            } else {
+                use TransportHeader as H;
+                use TransportSlice as S;
+                match &actual.transport {
+                    Some(S::Icmpv4(icmpv4)) => {
+                        assert_eq!(&test.transport, &Some(H::Icmpv4(icmpv4.header())));
+                        assert_eq!(icmpv4.payload(), expected_payload);
+                    }
+                    Some(S::Icmpv6(icmpv6)) => {
+                        assert_eq!(&test.transport, &Some(H::Icmpv6(icmpv6.header())));
+                        assert_eq!(icmpv6.payload(), expected_payload);
+                    }
+                    Some(S::Udp(s)) => {
+                        assert_eq!(&test.transport, &Some(H::Udp(s.to_header())));
+                    }
+                    Some(S::Tcp(s)) => {
+                        assert_eq!(&test.transport, &Some(H::Tcp(s.to_header())));
+                    }
+                    None => {
+                        assert_eq!(&test.transport, &None);
+                    }
+                }
+            }
+        }
+
+        // from_ethernet_slice
+        if test.link.is_some() {
+            if data.len() < Ethernet2Header::LEN {
+                assert_eq!(
+                    LenError {
+                        required_len: Ethernet2Header::LEN,
+                        len: data.len(),
+                        len_source: LenSource::Slice,
+                        layer: Layer::Ethernet2Header,
+                        layer_start_offset: 0
+                    },
+                    LaxSlicedPacket::from_ethernet(&data).unwrap_err()
+                );
+            } else {
+                let actual = LaxSlicedPacket::from_ethernet(&data).unwrap();
+                assert_eq!(actual.stop_err, expected_stop_err);
+                match expected_stop_err.as_ref().map(|v| v.1) {
+                    None => {
+                        assert_eq!(
+                            test.link,
+                            actual.link.as_ref().map(|v| v.to_header()).flatten()
+                        );
+                        compare_vlan(test, data, &actual);
+                        compare_ip(test, &actual);
+                        compare_transport(
+                            test,
+                            test.is_ip_payload_fragmented(),
+                            expected_payload,
+                            &actual,
+                        );
+                    }
+                    Some(Layer::VlanHeader) => {
+                        assert_eq!(
+                            test.link,
+                            actual.link.as_ref().map(|v| v.to_header()).flatten()
+                        );
+                        compare_vlan(test, data, &actual);
+                        assert_eq!(None, actual.net);
+                        assert_eq!(None, actual.transport);
+                    }
+                    Some(Layer::Ipv6Header) | Some(Layer::Ipv4Header) | Some(Layer::IpHeader) => {
+                        assert_eq!(
+                            test.link,
+                            actual.link.as_ref().map(|v| v.to_header()).flatten()
+                        );
+                        compare_vlan(test, data, &actual);
+                        assert_eq!(None, actual.net);
+                        assert_eq!(None, actual.transport);
+                    }
+                    Some(Layer::IpAuthHeader)
+                    | Some(Layer::Ipv6ExtHeader)
+                    | Some(Layer::Ipv6HopByHopHeader)
+                    | Some(Layer::Ipv6DestOptionsHeader)
+                    | Some(Layer::Ipv6RouteHeader)
+                    | Some(Layer::Ipv6FragHeader) => {
+                        assert_eq!(
+                            test.link,
+                            actual.link.as_ref().map(|v| v.to_header()).flatten()
+                        );
+                        compare_vlan(test, data, &actual);
+                        compare_ip_header_only(test, &actual);
+                        assert_eq!(None, actual.transport);
+                    }
+                    Some(Layer::TcpHeader)
+                    | Some(Layer::UdpHeader)
+                    | Some(Layer::Icmpv4)
+                    | Some(Layer::Icmpv6) => {
+                        assert_eq!(
+                            test.link,
+                            actual.link.as_ref().map(|v| v.to_header()).flatten()
+                        );
+                        compare_vlan(test, data, &actual);
+                        compare_ip(test, &actual);
+                        assert_eq!(None, actual.transport);
+                    }
+                    _ => unreachable!("error in an unexpected layer"),
+                }
+            }
+        }
+        // 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);
+                assert_eq!(actual.stop_err, expected_stop_err);
+                assert_eq!(
+                    Some(LinkSlice::EtherPayload(EtherPayloadSlice {
+                        ether_type,
+                        payload: data
+                    })),
+                    actual.link
+                );
+                compare_vlan(test, data, &actual);
+                match expected_stop_err.as_ref().map(|v| v.1) {
+                    None => {
+                        compare_ip(test, &actual);
+                        compare_transport(
+                            test,
+                            test.is_ip_payload_fragmented(),
+                            expected_payload,
+                            &actual,
+                        );
+                    }
+                    Some(Layer::VlanHeader) => {
+                        assert_eq!(None, actual.net);
+                        assert_eq!(None, actual.transport);
+                    }
+                    Some(Layer::Ipv6Header) | Some(Layer::Ipv4Header) | Some(Layer::IpHeader) => {
+                        assert_eq!(None, actual.net);
+                        assert_eq!(None, actual.transport);
+                    }
+                    Some(Layer::IpAuthHeader)
+                    | Some(Layer::Ipv6ExtHeader)
+                    | Some(Layer::Ipv6HopByHopHeader)
+                    | Some(Layer::Ipv6DestOptionsHeader)
+                    | Some(Layer::Ipv6RouteHeader)
+                    | Some(Layer::Ipv6FragHeader) => {
+                        compare_ip_header_only(test, &actual);
+                        assert_eq!(None, actual.transport);
+                    }
+                    Some(Layer::TcpHeader)
+                    | Some(Layer::UdpHeader)
+                    | Some(Layer::Icmpv4)
+                    | Some(Layer::Icmpv6) => {
+                        compare_ip(test, &actual);
+                        assert_eq!(None, actual.transport);
+                    }
+                    _ => unreachable!("error in an unexpected layer"),
+                }
+            }
+        }
+        // from_ether_type (ip at start)
+        if test.link.is_none() && test.vlan.is_none() {
+            if let Some(ip) = &test.net {
+                let ether_type = match ip {
+                    NetHeaders::Ipv4(_, _) => ether_type::IPV4,
+                    NetHeaders::Ipv6(_, _) => ether_type::IPV6,
+                };
+                let actual = LaxSlicedPacket::from_ether_type(ether_type, &data);
+                assert_eq!(actual.stop_err, expected_stop_err);
+                assert_eq!(
+                    Some(LinkSlice::EtherPayload(EtherPayloadSlice {
+                        ether_type,
+                        payload: data
+                    })),
+                    actual.link
+                );
+                assert_eq!(test.vlan, None);
+                match expected_stop_err.as_ref().map(|v| v.1) {
+                    None => {
+                        compare_ip(test, &actual);
+                        compare_transport(
+                            test,
+                            test.is_ip_payload_fragmented(),
+                            expected_payload,
+                            &actual,
+                        );
+                    }
+                    Some(Layer::Ipv6Header) | Some(Layer::Ipv4Header) | Some(Layer::IpHeader) => {
+                        assert_eq!(None, actual.net);
+                        assert_eq!(None, actual.transport);
+                    }
+                    Some(Layer::IpAuthHeader)
+                    | Some(Layer::Ipv6ExtHeader)
+                    | Some(Layer::Ipv6HopByHopHeader)
+                    | Some(Layer::Ipv6DestOptionsHeader)
+                    | Some(Layer::Ipv6RouteHeader)
+                    | Some(Layer::Ipv6FragHeader) => {
+                        compare_ip_header_only(test, &actual);
+                        assert_eq!(None, actual.transport);
+                    }
+                    Some(Layer::TcpHeader)
+                    | Some(Layer::UdpHeader)
+                    | Some(Layer::Icmpv4)
+                    | Some(Layer::Icmpv6) => {
+                        compare_ip(test, &actual);
+                        assert_eq!(None, actual.transport);
+                    }
+                    _ => unreachable!("error in an unexpected layer"),
+                }
+            }
+        }
+        // from_ip_slice
+        if test.link.is_none() && test.vlan.is_none() && test.net.is_some() {
+            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);
+                match expected_stop_err.as_ref().map(|v| v.1) {
+                    None => {
+                        compare_ip(test, &actual);
+                        compare_transport(
+                            test,
+                            test.is_ip_payload_fragmented(),
+                            expected_payload,
+                            &actual,
+                        );
+                    }
+                    Some(Layer::IpAuthHeader)
+                    | Some(Layer::Ipv6ExtHeader)
+                    | Some(Layer::Ipv6HopByHopHeader)
+                    | Some(Layer::Ipv6DestOptionsHeader)
+                    | Some(Layer::Ipv6RouteHeader)
+                    | Some(Layer::Ipv6FragHeader) => {
+                        compare_ip_header_only(test, &actual);
+                        assert_eq!(None, actual.transport);
+                    }
+                    Some(Layer::TcpHeader)
+                    | Some(Layer::UdpHeader)
+                    | Some(Layer::Icmpv4)
+                    | Some(Layer::Icmpv6) => {
+                        compare_ip(test, &actual);
+                        assert_eq!(None, actual.transport);
+                    }
+                    _ => unreachable!("error in an unexpected layer"),
+                }
+            }
+        }
+    }
+}
diff --git a/src/lax_sliced_packet_cursor.rs b/src/lax_sliced_packet_cursor.rs
new file mode 100644
index 0000000..5464c16
--- /dev/null
+++ b/src/lax_sliced_packet_cursor.rs
@@ -0,0 +1,281 @@
+use crate::{
+    err::{packet::SliceError, Layer},
+    *,
+};
+
+/// Helper class for laxly slicing packets.
+pub(crate) struct LaxSlicedPacketCursor<'a> {
+    pub offset: usize,
+    pub result: LaxSlicedPacket<'a>,
+}
+
+impl<'a> LaxSlicedPacketCursor<'a> {
+    pub fn parse_from_ethernet2(slice: &'a [u8]) -> Result<LaxSlicedPacket<'a>, err::LenError> {
+        use ether_type::*;
+        use LinkSlice::*;
+
+        let mut cursor = LaxSlicedPacketCursor {
+            offset: 0,
+            result: LaxSlicedPacket {
+                link: None,
+                vlan: None,
+                net: None,
+                transport: None,
+                stop_err: None,
+            },
+        };
+
+        let result = Ethernet2Slice::from_slice_without_fcs(slice)?;
+
+        // cache the ether_type for later
+        let payload = result.payload();
+
+        // set the new data
+        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),
+        }
+    }
+
+    pub fn parse_from_ether_type(ether_type: EtherType, slice: &'a [u8]) -> LaxSlicedPacket<'a> {
+        let cursor = LaxSlicedPacketCursor {
+            offset: 0,
+            result: LaxSlicedPacket {
+                link: Some(LinkSlice::EtherPayload(EtherPayloadSlice {
+                    ether_type,
+                    payload: slice,
+                })),
+                vlan: None,
+                net: None,
+                transport: None,
+                stop_err: None,
+            },
+        };
+        use ether_type::*;
+        match ether_type {
+            IPV4 => cursor.slice_ip(slice),
+            IPV6 => cursor.slice_ip(slice),
+            VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => {
+                cursor.slice_vlan(slice)
+            }
+            _ => cursor.result,
+        }
+    }
+
+    pub fn parse_from_ip(
+        slice: &'a [u8],
+    ) -> Result<LaxSlicedPacket<'a>, err::ip::LaxHeaderSliceError> {
+        let (ip, stop_err) = LaxIpSlice::from_slice(slice)?;
+        let is_ip_v4 = match &ip {
+            LaxIpSlice::Ipv4(_) => true,
+            LaxIpSlice::Ipv6(_) => false,
+        };
+        let payload = ip.payload().clone();
+        let offset = (payload.payload.as_ptr() as usize) - (slice.as_ptr() as usize);
+        Ok(LaxSlicedPacketCursor {
+            offset,
+            result: LaxSlicedPacket {
+                link: None,
+                vlan: None,
+                net: Some(ip.into()),
+                transport: None,
+                stop_err: stop_err.map(|(stop_err, stop_layer)| {
+                    use err::ipv6_exts::HeaderError as E;
+                    use err::ipv6_exts::HeaderSliceError as I;
+                    use err::packet::SliceError as O;
+                    (
+                        match stop_err {
+                            I::Len(l) => O::Len(l),
+                            I::Content(c) => match c {
+                                E::HopByHopNotAtStart => O::Ipv6Exts(E::HopByHopNotAtStart),
+                                E::IpAuth(auth) => {
+                                    if is_ip_v4 {
+                                        O::Ipv4Exts(auth)
+                                    } else {
+                                        O::Ipv6Exts(E::IpAuth(auth))
+                                    }
+                                }
+                            },
+                        },
+                        stop_layer,
+                    )
+                }),
+            },
+        }
+        .slice_transport(payload))
+    }
+
+    pub fn slice_vlan(mut self, slice: &'a [u8]) -> 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,
+                        ));
+                        return self.result;
+                    }
+                };
+                self.offset += inner.header_len();
+
+                let inner_ether_type = inner.ether_type();
+                self.result.vlan = Some(DoubleVlan(DoubleVlanSlice {
+                    slice: outer_start_slice,
+                }));
+
+                match inner_ether_type {
+                    IPV4 => self.slice_ip(inner.payload_slice()),
+                    IPV6 => self.slice_ip(inner.payload_slice()),
+                    _ => self.result,
+                }
+            }
+            value => match value {
+                IPV4 => self.slice_ip(outer.payload_slice()),
+                IPV6 => self.slice_ip(outer.payload_slice()),
+                _ => self.result,
+            },
+        }
+    }
+
+    pub fn slice_ip(mut self, slice: &'a [u8]) -> LaxSlicedPacket<'a> {
+        // ip slice
+        let ip = match LaxIpSlice::from_slice(slice) {
+            Ok(ip) => ip,
+            Err(e) => {
+                use err::ip::LaxHeaderSliceError as I;
+                use err::packet::SliceError as O;
+                self.result.stop_err = Some(match e {
+                    I::Len(mut l) => {
+                        l.layer_start_offset += self.offset;
+                        (O::Len(l), Layer::IpHeader)
+                    }
+                    I::Content(c) => (O::Ip(c), Layer::IpHeader),
+                });
+                return self.result;
+            }
+        };
+        self.result.net = Some(ip.0.clone().into());
+
+        // stop in case there was a stop error in the ip extension headers
+        if let Some((stop_err, stop_layer)) = ip.1 {
+            use err::ipv6_exts::HeaderError as E;
+            use err::ipv6_exts::HeaderSliceError as I;
+            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::Content(c) => match c {
+                        E::HopByHopNotAtStart => O::Ipv6Exts(E::HopByHopNotAtStart),
+                        E::IpAuth(auth) => match &ip.0 {
+                            LaxIpSlice::Ipv4(_) => O::Ipv4Exts(auth),
+                            LaxIpSlice::Ipv6(_) => O::Ipv6Exts(E::IpAuth(auth)),
+                        },
+                    },
+                },
+                stop_layer,
+            ));
+        }
+
+        // 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);
+        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
+            // stop here
+            return self.result;
+        }
+        match slice.ip_number {
+            ip_number::ICMP => match Icmpv4Slice::from_slice(slice.payload) {
+                Ok(icmp) => {
+                    self.offset += icmp.slice().len();
+                    self.result.transport = Some(TransportSlice::Icmpv4(icmp));
+                }
+                Err(mut err) => {
+                    err.layer_start_offset += self.offset;
+                    err.len_source = slice.len_source;
+                    self.result.stop_err = Some((O::Len(err), Layer::Icmpv4));
+                }
+            },
+            ip_number::UDP => match UdpSlice::from_slice_lax(slice.payload) {
+                Ok(udp) => {
+                    self.offset += udp.slice().len();
+                    self.result.transport = Some(TransportSlice::Udp(udp));
+                }
+                Err(mut err) => {
+                    err.layer_start_offset += self.offset;
+                    err.len_source = slice.len_source;
+                    self.result.stop_err = Some((O::Len(err), Layer::UdpHeader));
+                }
+            },
+            ip_number::TCP => match TcpSlice::from_slice(slice.payload) {
+                Ok(tcp) => {
+                    self.offset += tcp.slice().len();
+                    self.result.transport = Some(TransportSlice::Tcp(tcp));
+                }
+                Err(err) => {
+                    use err::tcp::HeaderSliceError as I;
+                    self.result.stop_err = Some((
+                        match err {
+                            I::Len(mut l) => {
+                                l.layer_start_offset += self.offset;
+                                l.len_source = slice.len_source;
+                                O::Len(l)
+                            }
+                            I::Content(c) => O::Tcp(c),
+                        },
+                        Layer::TcpHeader,
+                    ));
+                }
+            },
+            ip_number::IPV6_ICMP => match Icmpv6Slice::from_slice(slice.payload) {
+                Ok(icmp) => {
+                    self.offset += icmp.slice().len();
+                    self.result.transport = Some(TransportSlice::Icmpv6(icmp));
+                }
+                Err(mut err) => {
+                    err.layer_start_offset += self.offset;
+                    err.len_source = slice.len_source;
+                    self.result.stop_err = Some((O::Len(err), Layer::Icmpv6));
+                }
+            },
+            _ => {}
+        }
+        self.result
+    }
+}
diff --git a/src/len_source.rs b/src/len_source.rs
new file mode 100644
index 0000000..45d0039
--- /dev/null
+++ b/src/len_source.rs
@@ -0,0 +1,50 @@
+/// Sources of length limiting values (e.g. "ipv6 payload length field").
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub enum LenSource {
+    /// Limiting length was the slice length (we don't know what determined
+    /// that one originally).
+    Slice,
+    /// Length
+    Ipv4HeaderTotalLen,
+    /// Error occurred in the IPv6 layer.
+    Ipv6HeaderPayloadLen,
+    /// Error occurred while decoding an UDP header.
+    UdpHeaderLen,
+    /// Error occurred while decoding a TCP header.
+    TcpHeaderLen,
+}
+
+#[cfg(test)]
+mod test {
+    use super::LenSource::*;
+    use alloc::format;
+    use std::{
+        cmp::Ordering,
+        collections::hash_map::DefaultHasher,
+        hash::{Hash, Hasher},
+    };
+
+    #[test]
+    fn debug() {
+        assert_eq!("Slice", format!("{:?}", Slice));
+    }
+
+    #[test]
+    fn clone_eq_hash_ord() {
+        let layer = Slice;
+        assert_eq!(layer, layer.clone());
+        let hash_a = {
+            let mut hasher = DefaultHasher::new();
+            layer.hash(&mut hasher);
+            hasher.finish()
+        };
+        let hash_b = {
+            let mut hasher = DefaultHasher::new();
+            layer.clone().hash(&mut hasher);
+            hasher.finish()
+        };
+        assert_eq!(hash_a, hash_b);
+        assert_eq!(Ordering::Equal, layer.cmp(&layer));
+        assert_eq!(Some(Ordering::Equal), layer.partial_cmp(&layer));
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..2da16e7
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,431 @@
+//! A zero allocation supporting library for parsing & writing a bunch of packet based protocols (EthernetII, IPv4, IPv6, UDP, TCP ...).
+//!
+//! Currently supported are:
+//! * Ethernet II
+//! * IEEE 802.1Q VLAN Tagging Header
+//! * IPv4
+//! * IPv6 (supporting the most common extension headers, but not all)
+//! * UDP
+//! * TCP
+//! * ICMP & ICMPv6 (not all message types are supported)
+//!
+//! Reconstruction of fragmented IP packets is also supported, but requires allocations.
+//!
+//! # Usage
+//!
+//! Add the following to your `Cargo.toml`:
+//!
+//! ```toml
+//! [dependencies]
+//! etherparse = "0.16"
+//! ```
+//!
+//! # What is etherparse?
+//!
+//! Etherparse is intended to provide the basic network parsing functions that allow for easy analysis, transformation or generation of recorded network data.
+//!
+//! Some key points are:
+//!
+//! * It is completely written in Rust and thoroughly tested.
+//! * Special attention has been paid to not use allocations or syscalls.
+//! * The package is still in development and can & will still change.
+//! * The current focus of development is on the most popular protocols in the internet & transport layer.
+//!
+//! # How to parse network packages?
+//! Etherparse gives you two options for parsing network packages automatically:
+//!
+//! ## Slicing the packet
+//! Here the different components in a packet are separated without parsing all their fields. For each header a slice is generated that allows access to the fields of a header.
+//! ```
+//! # use etherparse::{SlicedPacket, PacketBuilder};
+//! # let builder = PacketBuilder::
+//! #    ethernet2([1,2,3,4,5,6],     //source mac
+//! #               [7,8,9,10,11,12]) //destination mac
+//! #    .ipv4([192,168,1,1], //source ip
+//! #          [192,168,1,2], //destination ip
+//! #          20)            //time to life
+//! #    .udp(21,    //source port
+//! #         1234); //destination port
+//! #    //payload of the udp packet
+//! #    let payload = [1,2,3,4,5,6,7,8];
+//! #    //get some memory to store the serialized data
+//! #    let mut packet = Vec::<u8>::with_capacity(
+//! #                            builder.size(payload.len()));
+//! #    builder.write(&mut packet, &payload).unwrap();
+//! match SlicedPacket::from_ethernet(&packet) {
+//!     Err(value) => println!("Err {:?}", value),
+//!     Ok(value) => {
+//!         println!("link: {:?}", value.link);
+//!         println!("vlan: {:?}", value.vlan);
+//!         println!("net: {:?}", value.net); // contains ip
+//!         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.
+//!
+//! Depending from which point downward you want to slice a package check out the functions:
+//!
+//! * [`SlicedPacket::from_ethernet`] for parsing from an Ethernet II header downwards
+//! * [`SlicedPacket::from_linux_sll`] for parsing from a Linux Cooked Capture v1 (SLL) downwards
+//! * [`SlicedPacket::from_ether_type`] for parsing a slice starting after an Ethernet II header
+//! * [`SlicedPacket::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`] for parsing from an Ethernet II header downwards
+//! * [`LaxSlicedPacket::from_ether_type`] for parsing a slice starting after an Ethernet II header
+//! * [`LaxSlicedPacket::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.
+//! ```rust
+//! # use etherparse::{PacketHeaders, PacketBuilder};
+//! # let builder = PacketBuilder::
+//! #    ethernet2([1,2,3,4,5,6],     //source mac
+//! #               [7,8,9,10,11,12]) //destination mac
+//! #    .ipv4([192,168,1,1], //source ip
+//! #          [192,168,1,2], //destination ip
+//! #          20)            //time to life
+//! #    .udp(21,    //source port
+//! #         1234); //destination port
+//! #    //payload of the udp packet
+//! #    let payload = [1,2,3,4,5,6,7,8];
+//! #    //get some memory to store the serialized data
+//! #    let mut packet = Vec::<u8>::with_capacity(
+//! #                            builder.size(payload.len()));
+//! #    builder.write(&mut packet, &payload).unwrap();
+//! match PacketHeaders::from_ethernet_slice(&packet) {
+//!     Err(value) => println!("Err {:?}", value),
+//!     Ok(value) => {
+//!         println!("link: {:?}", value.link);
+//!         println!("vlan: {:?}", value.vlan);
+//!         println!("net: {:?}", value.net); // contains ip
+//!         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.
+//!
+//! Depending from which point downward you want to unpack a package check out the functions
+//!
+//! * [`PacketHeaders::from_ethernet_slice`] for parsing from an Ethernet II header downwards
+//! * [`PacketHeaders::from_ether_type`] for parsing a slice starting after an Ethernet II header
+//! * [`PacketHeaders::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`] for parsing from an Ethernet II header downwards
+//! * [`LaxPacketHeaders::from_ether_type`] for parsing a slice starting after an Ethernet II header
+//! * [`LaxPacketHeaders::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`] & [`Ethernet2Slice::from_slice_with_crc32_fcs`]
+//! * [`LinuxSllSlice::from_slice`]
+//! * [`SingleVlanSlice::from_slice`] & [`DoubleVlanSlice::from_slice`]
+//! * [`IpSlice::from_slice`] & [`LaxIpSlice::from_slice`]
+//! * [`Ipv4Slice::from_slice`] & [`LaxIpv4Slice::from_slice`]
+//! * [`Ipv6Slice::from_slice`] & [`LaxIpv6Slice::from_slice`]
+//! * [`UdpSlice::from_slice`] & [`UdpSlice::from_slice_lax`]
+//! * [`TcpSlice::from_slice`]
+//! * [`Icmpv4Slice::from_slice`]
+//! * [`Icmpv6Slice::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
+//! payload (e.g. the payload of IPv6 packets will be limited by the "payload length" field in
+//! an IPv6 header).
+//!
+//! ## Manually slicing & parsing only headers
+//!
+//! 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`]
+//! * [`LinuxSllHeaderSlice::from_slice`]
+//! * [`SingleVlanHeaderSlice::from_slice`]
+//! * [`DoubleVlanHeaderSlice::from_slice`]
+//! * [`Ipv4HeaderSlice::from_slice`]
+//! * [`Ipv4ExtensionsSlice::from_slice`]
+//! * [`Ipv6HeaderSlice::from_slice`]
+//! * [`Ipv6ExtensionsSlice::from_slice`]
+//! * [`Ipv6RawExtHeaderSlice::from_slice`]
+//! * [`IpAuthHeaderSlice::from_slice`]
+//! * [`Ipv6FragmentHeaderSlice::from_slice`]
+//! * [`UdpHeaderSlice::from_slice`]
+//! * [`TcpHeaderSlice::from_slice`]
+//!
+//! And for deserialization into the corresponding header structs have a look at:
+//!
+//! * [`Ethernet2Header::read`] & [`Ethernet2Header::from_slice`]
+//! * [`LinuxSllHeader::read`] & [`LinuxSllHeader::from_slice`]
+//! * [`SingleVlanHeader::read`] & [`SingleVlanHeader::from_slice`]
+//! * [`DoubleVlanHeader::read`] & [`DoubleVlanHeader::from_slice`]
+//! * [`IpHeaders::read`] & [`IpHeaders::from_slice`]
+//! * [`Ipv4Header::read`] & [`Ipv4Header::from_slice`]
+//! * [`Ipv4Extensions::read`] & [`Ipv4Extensions::from_slice`]
+//! * [`Ipv6Header::read`] & [`Ipv6Header::from_slice`]
+//! * [`Ipv6Extensions::read`] & [`Ipv6Extensions::from_slice`]
+//! * [`Ipv6RawExtHeader::read`] & [`Ipv6RawExtHeader::from_slice`]
+//! * [`IpAuthHeader::read`] & [`IpAuthHeader::from_slice`]
+//! * [`Ipv6FragmentHeader::read`] & [`Ipv6FragmentHeader::from_slice`]
+//! * [`UdpHeader::read`] & [`UdpHeader::from_slice`]
+//! * [`TcpHeader::read`] & [`TcpHeader::from_slice`]
+//! * [`Icmpv4Header::read`] & [`Icmpv4Header::from_slice`]
+//! * [`Icmpv6Header::read`] & [`Icmpv6Header::from_slice`]
+//!
+//! # How to generate fake packet data?
+//!
+//! ## Packet Builder
+//!
+//! The PacketBuilder struct provides a high level interface for quickly creating network packets. The PacketBuilder will automatically set fields which can be deduced from the content and compositions of the packet itself (e.g. checksums, lengths, ethertype, ip protocol number).
+//!
+//! [Example:](https://github.com/JulianSchmid/etherparse/blob/0.14.3/examples/write_udp.rs)
+//! ```rust
+//! use etherparse::PacketBuilder;
+//!
+//! let builder = PacketBuilder::
+//!     ethernet2([1,2,3,4,5,6],     //source mac
+//!                [7,8,9,10,11,12]) //destination mac
+//!     .ipv4([192,168,1,1], //source ip
+//!           [192,168,1,2], //destination ip
+//!           20)            //time to life
+//!     .udp(21,    //source port
+//!          1234); //destination port
+//!
+//! //payload of the udp packet
+//! let payload = [1,2,3,4,5,6,7,8];
+//!
+//! //get some memory to store the result
+//! let mut result = Vec::<u8>::with_capacity(builder.size(payload.len()));
+//!
+//! //serialize
+//! //this will automatically set all length fields, checksums and identifiers (ethertype & protocol)
+//! //before writing the packet out to "result"
+//! builder.write(&mut result, &payload).unwrap();
+//! ```
+//!
+//! There is also an [example for TCP packets](https://github.com/JulianSchmid/etherparse/blob/0.14.3/examples/write_tcp.rs) available.
+//!
+//! Check out the [PacketBuilder documentation](struct.PacketBuilder.html) for more information.
+//!
+//! ## Manually serializing each header
+//!
+//! Alternatively it is possible to manually build a packet
+//! ([example](https://github.com/JulianSchmid/etherparse/blob/0.14.3/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`] & [`Ethernet2Header::write`]
+//! * [`LinuxSllHeader::to_bytes`] & [`LinuxSllHeader::write`]
+//! * [`SingleVlanHeader::to_bytes`] & [`SingleVlanHeader::write`]
+//! * [`DoubleVlanHeader::to_bytes`] & [`DoubleVlanHeader::write`]
+//! * [`Ipv4Header::to_bytes`] & [`Ipv4Header::write`] & [`Ipv4Header::write_raw`]
+//! * [`Ipv4Extensions::write`]
+//! * [`Ipv6Header::to_bytes`] & [`Ipv6Header::write`]
+//! * [`Ipv6Extensions::write`]
+//! * [`Ipv6RawExtHeader::to_bytes`] & [`Ipv6RawExtHeader::write`]
+//! * [`IpAuthHeader::to_bytes`] & [`IpAuthHeader::write`]
+//! * [`Ipv6FragmentHeader::to_bytes`] & [`Ipv6FragmentHeader::write`]
+//! * [`UdpHeader::to_bytes`] & [`UdpHeader::write`]
+//! * [`TcpHeader::to_bytes`] & [`TcpHeader::write`]
+//! * [`Icmpv4Header::to_bytes`] & [`Icmpv4Header::write`]
+//! * [`Icmpv6Header::to_bytes`] & [`Icmpv6Header::write`]
+//!
+//! # References
+//! * Darpa Internet Program Protocol Specification [RFC 791](https://tools.ietf.org/html/rfc791)
+//! * Internet Protocol, Version 6 (IPv6) Specification [RFC 8200](https://tools.ietf.org/html/rfc8200)
+//! * [IANA 802 EtherTypes](https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml)
+//! * [IANA Protocol Numbers](https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml)
+//! * [Internet Protocol Version 6 (IPv6) Parameters](https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml)
+//! * [Wikipedia IEEE_802.1Q](https://en.wikipedia.org/w/index.php?title=IEEE_802.1Q&oldid=820983900)
+//! * User Datagram Protocol (UDP) [RFC 768](https://tools.ietf.org/html/rfc768)
+//! * Transmission Control Protocol [RFC 793](https://tools.ietf.org/html/rfc793)
+//! * TCP Extensions for High Performance [RFC 7323](https://tools.ietf.org/html/rfc7323)
+//! * The Addition of Explicit Congestion Notification (ECN) to IP [RFC 3168](https://tools.ietf.org/html/rfc3168)
+//! * Robust Explicit Congestion Notification (ECN) Signaling with Nonces [RFC 3540](https://tools.ietf.org/html/rfc3540)
+//! * IP Authentication Header [RFC 4302](https://tools.ietf.org/html/rfc4302)
+//! * Mobility Support in IPv6 [RFC 6275](https://tools.ietf.org/html/rfc6275)
+//! * Host Identity Protocol Version 2 (HIPv2) [RFC 7401](https://tools.ietf.org/html/rfc7401)
+//! * Shim6: Level 3 Multihoming Shim Protocol for IPv6 [RFC 5533](https://tools.ietf.org/html/rfc5533)
+//! * Computing the Internet Checksum [RFC 1071](https://datatracker.ietf.org/doc/html/rfc1071)
+//! * Internet Control Message Protocol [RFC 792](https://datatracker.ietf.org/doc/html/rfc792)
+//! * [IANA Internet Control Message Protocol (ICMP) Parameters](https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml)
+//! * Requirements for Internet Hosts -- Communication Layers [RFC 1122](https://datatracker.ietf.org/doc/html/rfc1122)
+//! * Requirements for IP Version 4 Routers [RFC 1812](https://datatracker.ietf.org/doc/html/rfc1812)
+//! * Internet Control Message Protocol (ICMPv6) for the Internet Protocol Version 6 (IPv6) Specification [RFC 4443](https://datatracker.ietf.org/doc/html/rfc4443)
+//! * ICMP Router Discovery Messages [RFC 1256](https://datatracker.ietf.org/doc/html/rfc1256)
+//! * [Internet Control Message Protocol version 6 (ICMPv6) Parameters](https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml)
+//! * Multicast Listener Discovery (MLD) for IPv6 [RFC 2710](https://datatracker.ietf.org/doc/html/rfc2710)
+//! * Neighbor Discovery for IP version 6 (IPv6) [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)
+//! * [LINKTYPE_LINUX_SLL](https://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html) on tcpdump
+//! * LINUX_SLL [header definition](https://github.com/the-tcpdump-group/libpcap/blob/a932566fa1f6df16176ac702b1762ea1cd9ed9a3/pcap/sll.h) on libpcap
+//! * [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
+
+// # Reason for 'bool_comparison' disable:
+//
+// Clippy triggers triggers errors like the following if the warning stays enabled:
+//
+//   warning: equality checks against false can be replaced by a negation
+//     --> src/packet_decoder.rs:131:20
+//      |
+//  131 |                 if false == fragmented {
+//      |                    ^^^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!fragmented`
+//
+//
+// I prefer to write `false == value` instead of `!value` as it
+// is more visually striking and is not as easy to overlook as the single
+// character '!'.
+#![allow(clippy::bool_comparison)]
+// Removes all std and alloc default imports & enables "non std" support.
+#![no_std]
+// enables https://doc.rust-lang.org/beta/unstable-book/language-features/doc-cfg.html
+// for docs.rs
+#![cfg_attr(docsrs, feature(doc_cfg))]
+
+#[cfg(test)]
+extern crate alloc;
+#[cfg(test)]
+extern crate proptest;
+#[cfg(any(feature = "std", test))]
+extern crate std;
+
+/// Module containing error types that can be triggered.
+pub mod err;
+
+/// Module containing helpers to re-assemble fragmented packets (contains allocations).
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod defrag;
+
+mod link;
+pub use crate::link::arp_hardware_id::*;
+pub use crate::link::double_vlan_header::*;
+pub use crate::link::double_vlan_header_slice::*;
+pub use crate::link::double_vlan_slice::*;
+pub use crate::link::ether_payload_slice::*;
+pub use crate::link::ether_type_impl::*;
+pub use crate::link::ethernet2_header::*;
+pub use crate::link::ethernet2_header_slice::*;
+pub use crate::link::ethernet2_slice::*;
+pub use crate::link::link_header::*;
+pub use crate::link::link_slice::*;
+pub use crate::link::linux_nonstandard_ether_type::*;
+pub use crate::link::linux_sll_header::*;
+pub use crate::link::linux_sll_header_slice::*;
+pub use crate::link::linux_sll_packet_type::*;
+pub use crate::link::linux_sll_payload_slice::*;
+pub use crate::link::linux_sll_protocol_type::*;
+pub use crate::link::linux_sll_slice::*;
+pub use crate::link::single_vlan_header::*;
+pub use crate::link::single_vlan_header_slice::*;
+pub use crate::link::single_vlan_slice::*;
+pub use crate::link::vlan_header::*;
+pub use crate::link::vlan_id::*;
+pub use crate::link::vlan_pcp::*;
+pub use crate::link::vlan_slice::*;
+
+#[cfg(test)]
+pub(crate) mod test_gens;
+
+mod net;
+pub use net::*;
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub mod io;
+
+mod transport;
+pub use crate::transport::icmp_echo_header::*;
+pub use crate::transport::icmpv4;
+pub use crate::transport::icmpv4_header::*;
+pub use crate::transport::icmpv4_slice::*;
+pub use crate::transport::icmpv4_type::*;
+pub use crate::transport::icmpv6;
+pub use crate::transport::icmpv6_header::*;
+pub use crate::transport::icmpv6_slice::*;
+pub use crate::transport::icmpv6_type::*;
+pub use crate::transport::tcp_header::*;
+pub use crate::transport::tcp_header_slice::*;
+pub use crate::transport::tcp_option_element::*;
+pub use crate::transport::tcp_option_impl::*;
+pub use crate::transport::tcp_option_read_error::*;
+pub use crate::transport::tcp_option_write_error::*;
+pub use crate::transport::tcp_options::*;
+pub use crate::transport::tcp_options_iterator::*;
+pub use crate::transport::tcp_slice::*;
+pub use crate::transport::transport_header::*;
+pub use crate::transport::transport_slice::*;
+pub use crate::transport::udp_header::*;
+pub use crate::transport::udp_header_slice::*;
+pub use crate::transport::udp_slice::*;
+
+/// Helpers for calculating checksums.
+pub mod checksum;
+
+#[cfg(test)]
+mod compositions_tests;
+
+mod helpers;
+pub(crate) use helpers::*;
+
+mod lax_packet_headers;
+pub use lax_packet_headers::*;
+
+mod lax_payload_slice;
+pub use lax_payload_slice::*;
+
+mod lax_sliced_packet;
+pub use lax_sliced_packet::*;
+
+mod lax_sliced_packet_cursor;
+pub(crate) use lax_sliced_packet_cursor::*;
+
+mod len_source;
+pub use len_source::*;
+
+#[cfg(feature = "std")]
+mod packet_builder;
+#[cfg(feature = "std")]
+pub use crate::packet_builder::*;
+
+mod packet_headers;
+pub use crate::packet_headers::*;
+
+mod payload_slice;
+pub use crate::payload_slice::*;
+
+mod sliced_packet;
+pub use crate::sliced_packet::*;
+
+mod sliced_packet_cursor;
+pub(crate) use sliced_packet_cursor::*;
+
+#[cfg(test)]
+pub(crate) mod test_packet;
+
+/// Deprecated use [err::ReadError] instead or use the specific error type returned by operation you are using.
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+#[deprecated(
+    since = "0.14.0",
+    note = "Please use the type err::ReadError instead or use the specific error type returned by operation you are using."
+)]
+pub type ReadError = err::ReadError;
+
+/// Deprecated use [err::ReadError] instead or use the specific error type returned by operation you are using.
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+#[deprecated(since = "0.14.0", note = "Please use the type err::Field instead.")]
+pub type ErrorField = err::ValueType;
diff --git a/src/link/arp_hardware_id.rs b/src/link/arp_hardware_id.rs
new file mode 100644
index 0000000..2f89ce4
--- /dev/null
+++ b/src/link/arp_hardware_id.rs
@@ -0,0 +1,532 @@
+/// Represents an ARP protocol hardware identifier.
+///
+/// You can access the underlying `u16` value by using `.0` and any `u16`
+/// can be converted to an `ArpHardwareId`:
+///
+/// ```
+/// use etherparse::ArpHardwareId;
+///
+/// assert_eq!(ArpHardwareId::ETHER.0, 0x0001);
+/// assert_eq!(ArpHardwareId::ETHER, ArpHardwareId(0x0001));
+///
+/// // convert to ArpHardwareId using the from & into trait
+/// let arp_hrd_id: ArpHardwareId = 0x0001.into();
+/// assert_eq!(ArpHardwareId::ETHER, arp_hrd_id);
+///
+/// // convert to u16 using the from & into trait
+/// let num: u16 = ArpHardwareId::ETHER.into();
+/// assert_eq!(0x0001, num);
+/// ```
+///
+
+#[derive(Clone, Copy, Eq, PartialEq, Default, Hash)]
+pub struct ArpHardwareId(pub u16);
+
+impl ArpHardwareId {
+    // Numbers sourced from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/if_arp.h?id=e33c4963bf536900f917fb65a687724d5539bc21
+
+    pub const NETROM: ArpHardwareId = Self(0);
+    pub const ETHER: ArpHardwareId = Self(1);
+    pub const EETHER: ArpHardwareId = Self(2);
+    pub const AX25: ArpHardwareId = Self(3);
+    pub const PRONET: ArpHardwareId = Self(4);
+    pub const CHAOS: ArpHardwareId = Self(5);
+    pub const IEEE802: ArpHardwareId = Self(6);
+    pub const ARCNET: ArpHardwareId = Self(7);
+    pub const APPLETLK: ArpHardwareId = Self(8);
+    pub const DLCI: ArpHardwareId = Self(15);
+    pub const ATM: ArpHardwareId = Self(19);
+    pub const METRICOM: ArpHardwareId = Self(23);
+    pub const IEEE1394: ArpHardwareId = Self(24);
+    pub const EUI64: ArpHardwareId = Self(27);
+    pub const INFINIBAND: ArpHardwareId = Self(32);
+    pub const SLIP: ArpHardwareId = Self(256);
+    pub const CSLIP: ArpHardwareId = Self(257);
+    pub const SLIP6: ArpHardwareId = Self(258);
+    pub const CSLIP6: ArpHardwareId = Self(259);
+    pub const RSRVD: ArpHardwareId = Self(260);
+    pub const ADAPT: ArpHardwareId = Self(264);
+    pub const ROSE: ArpHardwareId = Self(270);
+    pub const X25: ArpHardwareId = Self(271);
+    pub const HWX25: ArpHardwareId = Self(272);
+    pub const CAN: ArpHardwareId = Self(280);
+    pub const PPP: ArpHardwareId = Self(512);
+    pub const CISCO_HDLC: ArpHardwareId = Self(513);
+    pub const LAPB: ArpHardwareId = Self(516);
+    pub const DDCMP: ArpHardwareId = Self(517);
+    pub const RAWHDLC: ArpHardwareId = Self(518);
+    pub const RAWIP: ArpHardwareId = Self(519);
+    pub const TUNNEL: ArpHardwareId = Self(768);
+    pub const TUNNEL6: ArpHardwareId = Self(769);
+    pub const FRAD: ArpHardwareId = Self(770);
+    pub const SKIP: ArpHardwareId = Self(771);
+    pub const LOOPBACK: ArpHardwareId = Self(772);
+    pub const LOCALTLK: ArpHardwareId = Self(773);
+    pub const FDDI: ArpHardwareId = Self(774);
+    pub const BIF: ArpHardwareId = Self(775);
+    pub const SIT: ArpHardwareId = Self(776);
+    pub const IPDDP: ArpHardwareId = Self(777);
+    pub const IPGRE: ArpHardwareId = Self(778);
+    pub const PIMREG: ArpHardwareId = Self(779);
+    pub const HIPPI: ArpHardwareId = Self(780);
+    pub const ASH: ArpHardwareId = Self(781);
+    pub const ECONET: ArpHardwareId = Self(782);
+    pub const IRDA: ArpHardwareId = Self(783);
+    pub const FCPP: ArpHardwareId = Self(784);
+    pub const FCAL: ArpHardwareId = Self(785);
+    pub const FCPL: ArpHardwareId = Self(786);
+    pub const FCFABRIC: ArpHardwareId = Self(787);
+    pub const IEEE802_TR: ArpHardwareId = Self(800);
+    pub const IEEE80211: ArpHardwareId = Self(801);
+    pub const IEEE80211_PRISM: ArpHardwareId = Self(802);
+    pub const IEEE80211_RADIOTAP: ArpHardwareId = Self(803);
+    pub const IEEE802154: ArpHardwareId = Self(804);
+    pub const IEEE802154_MONITOR: ArpHardwareId = Self(805);
+    pub const PHONET: ArpHardwareId = Self(820);
+    pub const PHONET_PIPE: ArpHardwareId = Self(821);
+    pub const CAIF: ArpHardwareId = Self(822);
+    pub const IP6GRE: ArpHardwareId = Self(823);
+    pub const NETLINK: ArpHardwareId = Self(824);
+    pub const IPV6LOWPAN: ArpHardwareId = Self(825);
+    pub const VSOCKMON: ArpHardwareId = Self(826);
+    pub const VOID: ArpHardwareId = Self(0xFFFF);
+    pub const NONE: ArpHardwareId = Self(0xFFFE);
+}
+
+impl From<u16> for ArpHardwareId {
+    #[inline]
+    fn from(val: u16) -> Self {
+        ArpHardwareId(val)
+    }
+}
+
+impl From<ArpHardwareId> for u16 {
+    #[inline]
+    fn from(val: ArpHardwareId) -> Self {
+        val.0
+    }
+}
+
+impl core::fmt::Display for ArpHardwareId {
+    // Names sourced from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/if_arp.h?id=e33c4963bf536900f917fb65a687724d5539bc21
+
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        match *self {
+            Self::NETROM => write!(f, "{} (from KA9Q: NET/ROM pseudo)", self.0),
+            Self::ETHER => write!(f, "{} (Ethernet 10Mbps)", self.0),
+            Self::EETHER => write!(f, "{} (Experimental Ethernet)", self.0),
+            Self::AX25 => write!(f, "{} (AX.25 Level 2)", self.0),
+            Self::PRONET => write!(f, "{} (PROnet token ring)", self.0),
+            Self::CHAOS => write!(f, "{} (Chaosnet)", self.0),
+            Self::IEEE802 => write!(f, "{} (IEEE 802.2 Ethernet/TR/TB)", self.0),
+            Self::ARCNET => write!(f, "{} (ARCnet)", self.0),
+            Self::APPLETLK => write!(f, "{} (APPLEtalk)", self.0),
+            Self::DLCI => write!(f, "{} (Frame Relay DLCI)", self.0),
+            Self::ATM => write!(f, "{} (ATM)", self.0),
+            Self::METRICOM => write!(f, "{} (Metricom STRIP (new IANA id))", self.0),
+            Self::IEEE1394 => write!(f, "{} (IEEE 1394 IPv4 - RFC 2734)", self.0),
+            Self::EUI64 => write!(f, "{} (EUI-64)", self.0),
+            Self::INFINIBAND => write!(f, "{} (InfiniBand)", self.0),
+            Self::SLIP => write!(f, "{} (SLIP)", self.0),
+            Self::CSLIP => write!(f, "{} (CSLIP)", self.0),
+            Self::SLIP6 => write!(f, "{} (SLIP6)", self.0),
+            Self::CSLIP6 => write!(f, "{} (CSLIP6)", self.0),
+            Self::RSRVD => write!(f, "{} (Notional KISS type)", self.0),
+            Self::ADAPT => write!(f, "{} (ADAPT)", self.0),
+            Self::ROSE => write!(f, "{} (ROSE)", self.0),
+            Self::X25 => write!(f, "{} (CCITT X.25)", self.0),
+            Self::HWX25 => write!(f, "{} (Boards with X.25 in firmware)", self.0),
+            Self::CAN => write!(f, "{} (Controller Area Network)", self.0),
+            Self::PPP => write!(f, "{} (PPP)", self.0),
+            Self::CISCO_HDLC => write!(f, "{} (Cisco HDLC)", self.0),
+            Self::LAPB => write!(f, "{} (LAPB)", self.0),
+            Self::DDCMP => write!(f, "{} (Digital's DDCMP protocol)", self.0),
+            Self::RAWHDLC => write!(f, "{} (Raw HDLC)", self.0),
+            Self::RAWIP => write!(f, "{} (Raw IP)", self.0),
+            Self::TUNNEL => write!(f, "{} (IPIP tunnel)", self.0),
+            Self::TUNNEL6 => write!(f, "{} (IP6IP6 tunnel)", self.0),
+            Self::FRAD => write!(f, "{} (Frame Relay Access Device)", self.0),
+            Self::SKIP => write!(f, "{} (SKIP vif)", self.0),
+            Self::LOOPBACK => write!(f, "{} (Loopback device)", self.0),
+            Self::LOCALTLK => write!(f, "{} (Localtalk device)", self.0),
+            Self::FDDI => write!(f, "{} (Fiber Distributed Data Interface)", self.0),
+            Self::BIF => write!(f, "{} (AP1000 BIF)", self.0),
+            Self::SIT => write!(f, "{} (sit0 device - IPv6-in-IPv4)", self.0),
+            Self::IPDDP => write!(f, "{} (IP over DDP tunneller)", self.0),
+            Self::IPGRE => write!(f, "{} (GRE over IP)", self.0),
+            Self::PIMREG => write!(f, "{} (PIMSM register interface)", self.0),
+            Self::HIPPI => write!(f, "{} (High Performance Parallel Interface)", self.0),
+            Self::ASH => write!(f, "{} (Nexus 64Mbps Ash)", self.0),
+            Self::ECONET => write!(f, "{} (Acorn Econet)", self.0),
+            Self::IRDA => write!(f, "{} (Linux-IrDA)", self.0),
+            Self::FCPP => write!(f, "{} (Point to point fibrechannel)", self.0),
+            Self::FCAL => write!(f, "{} (Fibrechannel arbitrated loop)", self.0),
+            Self::FCPL => write!(f, "{} (Fibrechannel public loop)", self.0),
+            Self::FCFABRIC => write!(f, "{} (Fibrechannel fabric)", self.0),
+            Self::IEEE802_TR => write!(f, "{} (Magic type ident for TR)", self.0),
+            Self::IEEE80211 => write!(f, "{} (IEEE 802.11)", self.0),
+            Self::IEEE80211_PRISM => write!(f, "{} (IEEE 802.11 + Prism2 header)", self.0),
+            Self::IEEE80211_RADIOTAP => write!(f, "{} (IEEE 802.11 + radiotap header)", self.0),
+            Self::IEEE802154 => write!(f, "{} (IEEE 802.15.4)", self.0),
+            Self::IEEE802154_MONITOR => write!(f, "{} (IEEE 802.15.4 network monitor)", self.0),
+            Self::PHONET => write!(f, "{} (PhoNet media type)", self.0),
+            Self::PHONET_PIPE => write!(f, "{} (PhoNet pipe header)", self.0),
+            Self::CAIF => write!(f, "{} (CAIF media type)", self.0),
+            Self::IP6GRE => write!(f, "{} (GRE over IPv6)", self.0),
+            Self::NETLINK => write!(f, "{} (Netlink header)", self.0),
+            Self::IPV6LOWPAN => write!(f, "{} (IPv6 over LoWPAN)", self.0),
+            Self::VSOCKMON => write!(f, "{} (Vsock monitor header)", self.0),
+            Self::VOID => write!(f, "{:#06X} (Void type, nothing is known)", self.0),
+            Self::NONE => write!(f, "{:#06X} (zero header length)", self.0),
+            _ => write!(f, "{:#06X}", self.0),
+        }
+    }
+}
+
+impl core::fmt::Debug for ArpHardwareId {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        core::fmt::Display::fmt(&self, f)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use alloc::format;
+
+    #[test]
+    fn to_u16() {
+        assert_eq!(0, u16::from(ArpHardwareId::NETROM));
+        assert_eq!(1, u16::from(ArpHardwareId::ETHER));
+        assert_eq!(2, u16::from(ArpHardwareId::EETHER));
+        assert_eq!(3, u16::from(ArpHardwareId::AX25));
+        assert_eq!(4, u16::from(ArpHardwareId::PRONET));
+        assert_eq!(5, u16::from(ArpHardwareId::CHAOS));
+        assert_eq!(6, u16::from(ArpHardwareId::IEEE802));
+        assert_eq!(7, u16::from(ArpHardwareId::ARCNET));
+        assert_eq!(8, u16::from(ArpHardwareId::APPLETLK));
+        assert_eq!(15, u16::from(ArpHardwareId::DLCI));
+        assert_eq!(19, u16::from(ArpHardwareId::ATM));
+        assert_eq!(23, u16::from(ArpHardwareId::METRICOM));
+        assert_eq!(24, u16::from(ArpHardwareId::IEEE1394));
+        assert_eq!(27, u16::from(ArpHardwareId::EUI64));
+        assert_eq!(32, u16::from(ArpHardwareId::INFINIBAND));
+
+        assert_eq!(256, u16::from(ArpHardwareId::SLIP));
+        assert_eq!(257, u16::from(ArpHardwareId::CSLIP));
+        assert_eq!(258, u16::from(ArpHardwareId::SLIP6));
+        assert_eq!(259, u16::from(ArpHardwareId::CSLIP6));
+        assert_eq!(260, u16::from(ArpHardwareId::RSRVD));
+        assert_eq!(264, u16::from(ArpHardwareId::ADAPT));
+        assert_eq!(270, u16::from(ArpHardwareId::ROSE));
+        assert_eq!(271, u16::from(ArpHardwareId::X25));
+        assert_eq!(272, u16::from(ArpHardwareId::HWX25));
+        assert_eq!(280, u16::from(ArpHardwareId::CAN));
+        assert_eq!(512, u16::from(ArpHardwareId::PPP));
+        assert_eq!(513, u16::from(ArpHardwareId::CISCO_HDLC));
+        assert_eq!(516, u16::from(ArpHardwareId::LAPB));
+        assert_eq!(517, u16::from(ArpHardwareId::DDCMP));
+        assert_eq!(518, u16::from(ArpHardwareId::RAWHDLC));
+        assert_eq!(519, u16::from(ArpHardwareId::RAWIP));
+
+        assert_eq!(768, u16::from(ArpHardwareId::TUNNEL));
+        assert_eq!(769, u16::from(ArpHardwareId::TUNNEL6));
+        assert_eq!(770, u16::from(ArpHardwareId::FRAD));
+        assert_eq!(771, u16::from(ArpHardwareId::SKIP));
+        assert_eq!(772, u16::from(ArpHardwareId::LOOPBACK));
+        assert_eq!(773, u16::from(ArpHardwareId::LOCALTLK));
+        assert_eq!(774, u16::from(ArpHardwareId::FDDI));
+        assert_eq!(775, u16::from(ArpHardwareId::BIF));
+        assert_eq!(776, u16::from(ArpHardwareId::SIT));
+        assert_eq!(777, u16::from(ArpHardwareId::IPDDP));
+        assert_eq!(778, u16::from(ArpHardwareId::IPGRE));
+        assert_eq!(779, u16::from(ArpHardwareId::PIMREG));
+        assert_eq!(780, u16::from(ArpHardwareId::HIPPI));
+        assert_eq!(781, u16::from(ArpHardwareId::ASH));
+        assert_eq!(782, u16::from(ArpHardwareId::ECONET));
+        assert_eq!(783, u16::from(ArpHardwareId::IRDA));
+
+        assert_eq!(784, u16::from(ArpHardwareId::FCPP));
+        assert_eq!(785, u16::from(ArpHardwareId::FCAL));
+        assert_eq!(786, u16::from(ArpHardwareId::FCPL));
+        assert_eq!(787, u16::from(ArpHardwareId::FCFABRIC));
+
+        assert_eq!(800, u16::from(ArpHardwareId::IEEE802_TR));
+        assert_eq!(801, u16::from(ArpHardwareId::IEEE80211));
+        assert_eq!(802, u16::from(ArpHardwareId::IEEE80211_PRISM));
+        assert_eq!(803, u16::from(ArpHardwareId::IEEE80211_RADIOTAP));
+        assert_eq!(804, u16::from(ArpHardwareId::IEEE802154));
+        assert_eq!(805, u16::from(ArpHardwareId::IEEE802154_MONITOR));
+
+        assert_eq!(820, u16::from(ArpHardwareId::PHONET));
+        assert_eq!(821, u16::from(ArpHardwareId::PHONET_PIPE));
+        assert_eq!(822, u16::from(ArpHardwareId::CAIF));
+        assert_eq!(823, u16::from(ArpHardwareId::IP6GRE));
+        assert_eq!(824, u16::from(ArpHardwareId::NETLINK));
+        assert_eq!(825, u16::from(ArpHardwareId::IPV6LOWPAN));
+        assert_eq!(826, u16::from(ArpHardwareId::VSOCKMON));
+
+        assert_eq!(0xFFFF, u16::from(ArpHardwareId::VOID));
+        assert_eq!(0xFFFE, u16::from(ArpHardwareId::NONE));
+    }
+
+    #[test]
+    fn from_u16() {
+        assert_eq!(ArpHardwareId::from(0), ArpHardwareId::NETROM);
+        assert_eq!(ArpHardwareId::from(1), ArpHardwareId::ETHER);
+        assert_eq!(ArpHardwareId::from(2), ArpHardwareId::EETHER);
+        assert_eq!(ArpHardwareId::from(3), ArpHardwareId::AX25);
+        assert_eq!(ArpHardwareId::from(4), ArpHardwareId::PRONET);
+        assert_eq!(ArpHardwareId::from(5), ArpHardwareId::CHAOS);
+        assert_eq!(ArpHardwareId::from(6), ArpHardwareId::IEEE802);
+        assert_eq!(ArpHardwareId::from(7), ArpHardwareId::ARCNET);
+        assert_eq!(ArpHardwareId::from(8), ArpHardwareId::APPLETLK);
+        assert_eq!(ArpHardwareId::from(15), ArpHardwareId::DLCI);
+        assert_eq!(ArpHardwareId::from(19), ArpHardwareId::ATM);
+        assert_eq!(ArpHardwareId::from(23), ArpHardwareId::METRICOM);
+        assert_eq!(ArpHardwareId::from(24), ArpHardwareId::IEEE1394);
+        assert_eq!(ArpHardwareId::from(27), ArpHardwareId::EUI64);
+        assert_eq!(ArpHardwareId::from(32), ArpHardwareId::INFINIBAND);
+
+        assert_eq!(ArpHardwareId::from(256), ArpHardwareId::SLIP);
+        assert_eq!(ArpHardwareId::from(257), ArpHardwareId::CSLIP);
+        assert_eq!(ArpHardwareId::from(258), ArpHardwareId::SLIP6);
+        assert_eq!(ArpHardwareId::from(259), ArpHardwareId::CSLIP6);
+        assert_eq!(ArpHardwareId::from(260), ArpHardwareId::RSRVD);
+        assert_eq!(ArpHardwareId::from(264), ArpHardwareId::ADAPT);
+        assert_eq!(ArpHardwareId::from(270), ArpHardwareId::ROSE);
+        assert_eq!(ArpHardwareId::from(271), ArpHardwareId::X25);
+        assert_eq!(ArpHardwareId::from(272), ArpHardwareId::HWX25);
+        assert_eq!(ArpHardwareId::from(280), ArpHardwareId::CAN);
+        assert_eq!(ArpHardwareId::from(512), ArpHardwareId::PPP);
+        assert_eq!(ArpHardwareId::from(513), ArpHardwareId::CISCO_HDLC);
+        assert_eq!(ArpHardwareId::from(516), ArpHardwareId::LAPB);
+        assert_eq!(ArpHardwareId::from(517), ArpHardwareId::DDCMP);
+        assert_eq!(ArpHardwareId::from(518), ArpHardwareId::RAWHDLC);
+        assert_eq!(ArpHardwareId::from(519), ArpHardwareId::RAWIP);
+
+        assert_eq!(ArpHardwareId::from(768), ArpHardwareId::TUNNEL);
+        assert_eq!(ArpHardwareId::from(769), ArpHardwareId::TUNNEL6);
+        assert_eq!(ArpHardwareId::from(770), ArpHardwareId::FRAD);
+        assert_eq!(ArpHardwareId::from(771), ArpHardwareId::SKIP);
+        assert_eq!(ArpHardwareId::from(772), ArpHardwareId::LOOPBACK);
+        assert_eq!(ArpHardwareId::from(773), ArpHardwareId::LOCALTLK);
+        assert_eq!(ArpHardwareId::from(774), ArpHardwareId::FDDI);
+        assert_eq!(ArpHardwareId::from(775), ArpHardwareId::BIF);
+        assert_eq!(ArpHardwareId::from(776), ArpHardwareId::SIT);
+        assert_eq!(ArpHardwareId::from(777), ArpHardwareId::IPDDP);
+        assert_eq!(ArpHardwareId::from(778), ArpHardwareId::IPGRE);
+        assert_eq!(ArpHardwareId::from(779), ArpHardwareId::PIMREG);
+        assert_eq!(ArpHardwareId::from(780), ArpHardwareId::HIPPI);
+        assert_eq!(ArpHardwareId::from(781), ArpHardwareId::ASH);
+        assert_eq!(ArpHardwareId::from(782), ArpHardwareId::ECONET);
+        assert_eq!(ArpHardwareId::from(783), ArpHardwareId::IRDA);
+
+        assert_eq!(ArpHardwareId::from(784), ArpHardwareId::FCPP);
+        assert_eq!(ArpHardwareId::from(785), ArpHardwareId::FCAL);
+        assert_eq!(ArpHardwareId::from(786), ArpHardwareId::FCPL);
+        assert_eq!(ArpHardwareId::from(787), ArpHardwareId::FCFABRIC);
+
+        assert_eq!(ArpHardwareId::from(800), ArpHardwareId::IEEE802_TR);
+        assert_eq!(ArpHardwareId::from(801), ArpHardwareId::IEEE80211);
+        assert_eq!(ArpHardwareId::from(802), ArpHardwareId::IEEE80211_PRISM);
+        assert_eq!(ArpHardwareId::from(803), ArpHardwareId::IEEE80211_RADIOTAP);
+        assert_eq!(ArpHardwareId::from(804), ArpHardwareId::IEEE802154);
+        assert_eq!(ArpHardwareId::from(805), ArpHardwareId::IEEE802154_MONITOR);
+
+        assert_eq!(ArpHardwareId::from(820), ArpHardwareId::PHONET);
+        assert_eq!(ArpHardwareId::from(821), ArpHardwareId::PHONET_PIPE);
+        assert_eq!(ArpHardwareId::from(822), ArpHardwareId::CAIF);
+        assert_eq!(ArpHardwareId::from(823), ArpHardwareId::IP6GRE);
+        assert_eq!(ArpHardwareId::from(824), ArpHardwareId::NETLINK);
+        assert_eq!(ArpHardwareId::from(825), ArpHardwareId::IPV6LOWPAN);
+        assert_eq!(ArpHardwareId::from(826), ArpHardwareId::VSOCKMON);
+
+        assert_eq!(ArpHardwareId::from(0xFFFF), ArpHardwareId::VOID);
+        assert_eq!(ArpHardwareId::from(0xFFFE), ArpHardwareId::NONE);
+    }
+
+    #[test]
+    fn display_dbg() {
+        let pairs = &[
+            (ArpHardwareId::NETROM, "0 (from KA9Q: NET/ROM pseudo)"),
+            (ArpHardwareId::ETHER, "1 (Ethernet 10Mbps)"),
+            (ArpHardwareId::EETHER, "2 (Experimental Ethernet)"),
+            (ArpHardwareId::AX25, "3 (AX.25 Level 2)"),
+            (ArpHardwareId::PRONET, "4 (PROnet token ring)"),
+            (ArpHardwareId::CHAOS, "5 (Chaosnet)"),
+            (ArpHardwareId::IEEE802, "6 (IEEE 802.2 Ethernet/TR/TB)"),
+            (ArpHardwareId::ARCNET, "7 (ARCnet)"),
+            (ArpHardwareId::APPLETLK, "8 (APPLEtalk)"),
+            (ArpHardwareId::DLCI, "15 (Frame Relay DLCI)"),
+            (ArpHardwareId::ATM, "19 (ATM)"),
+            (ArpHardwareId::METRICOM, "23 (Metricom STRIP (new IANA id))"),
+            (ArpHardwareId::IEEE1394, "24 (IEEE 1394 IPv4 - RFC 2734)"),
+            (ArpHardwareId::EUI64, "27 (EUI-64)"),
+            (ArpHardwareId::INFINIBAND, "32 (InfiniBand)"),
+            (ArpHardwareId::SLIP, "256 (SLIP)"),
+            (ArpHardwareId::CSLIP, "257 (CSLIP)"),
+            (ArpHardwareId::SLIP6, "258 (SLIP6)"),
+            (ArpHardwareId::CSLIP6, "259 (CSLIP6)"),
+            (ArpHardwareId::RSRVD, "260 (Notional KISS type)"),
+            (ArpHardwareId::ADAPT, "264 (ADAPT)"),
+            (ArpHardwareId::ROSE, "270 (ROSE)"),
+            (ArpHardwareId::X25, "271 (CCITT X.25)"),
+            (ArpHardwareId::HWX25, "272 (Boards with X.25 in firmware)"),
+            (ArpHardwareId::CAN, "280 (Controller Area Network)"),
+            (ArpHardwareId::PPP, "512 (PPP)"),
+            (ArpHardwareId::CISCO_HDLC, "513 (Cisco HDLC)"),
+            (ArpHardwareId::LAPB, "516 (LAPB)"),
+            (ArpHardwareId::DDCMP, "517 (Digital's DDCMP protocol)"),
+            (ArpHardwareId::RAWHDLC, "518 (Raw HDLC)"),
+            (ArpHardwareId::RAWIP, "519 (Raw IP)"),
+            (ArpHardwareId::TUNNEL, "768 (IPIP tunnel)"),
+            (ArpHardwareId::TUNNEL6, "769 (IP6IP6 tunnel)"),
+            (ArpHardwareId::FRAD, "770 (Frame Relay Access Device)"),
+            (ArpHardwareId::SKIP, "771 (SKIP vif)"),
+            (ArpHardwareId::LOOPBACK, "772 (Loopback device)"),
+            (ArpHardwareId::LOCALTLK, "773 (Localtalk device)"),
+            (
+                ArpHardwareId::FDDI,
+                "774 (Fiber Distributed Data Interface)",
+            ),
+            (ArpHardwareId::BIF, "775 (AP1000 BIF)"),
+            (ArpHardwareId::SIT, "776 (sit0 device - IPv6-in-IPv4)"),
+            (ArpHardwareId::IPDDP, "777 (IP over DDP tunneller)"),
+            (ArpHardwareId::IPGRE, "778 (GRE over IP)"),
+            (ArpHardwareId::PIMREG, "779 (PIMSM register interface)"),
+            (
+                ArpHardwareId::HIPPI,
+                "780 (High Performance Parallel Interface)",
+            ),
+            (ArpHardwareId::ASH, "781 (Nexus 64Mbps Ash)"),
+            (ArpHardwareId::ECONET, "782 (Acorn Econet)"),
+            (ArpHardwareId::IRDA, "783 (Linux-IrDA)"),
+            (ArpHardwareId::FCPP, "784 (Point to point fibrechannel)"),
+            (ArpHardwareId::FCAL, "785 (Fibrechannel arbitrated loop)"),
+            (ArpHardwareId::FCPL, "786 (Fibrechannel public loop)"),
+            (ArpHardwareId::FCFABRIC, "787 (Fibrechannel fabric)"),
+            (ArpHardwareId::IEEE802_TR, "800 (Magic type ident for TR)"),
+            (ArpHardwareId::IEEE80211, "801 (IEEE 802.11)"),
+            (
+                ArpHardwareId::IEEE80211_PRISM,
+                "802 (IEEE 802.11 + Prism2 header)",
+            ),
+            (
+                ArpHardwareId::IEEE80211_RADIOTAP,
+                "803 (IEEE 802.11 + radiotap header)",
+            ),
+            (ArpHardwareId::IEEE802154, "804 (IEEE 802.15.4)"),
+            (
+                ArpHardwareId::IEEE802154_MONITOR,
+                "805 (IEEE 802.15.4 network monitor)",
+            ),
+            (ArpHardwareId::PHONET, "820 (PhoNet media type)"),
+            (ArpHardwareId::PHONET_PIPE, "821 (PhoNet pipe header)"),
+            (ArpHardwareId::CAIF, "822 (CAIF media type)"),
+            (ArpHardwareId::IP6GRE, "823 (GRE over IPv6)"),
+            (ArpHardwareId::NETLINK, "824 (Netlink header)"),
+            (ArpHardwareId::IPV6LOWPAN, "825 (IPv6 over LoWPAN)"),
+            (ArpHardwareId::VSOCKMON, "826 (Vsock monitor header)"),
+            (ArpHardwareId::VOID, "0xFFFF (Void type, nothing is known)"),
+            (ArpHardwareId::NONE, "0xFFFE (zero header length)"),
+            (ArpHardwareId::from(0x1234), "0x1234"),
+        ];
+
+        for (ether_type, str_value) in pairs {
+            assert_eq!(str_value, &format!("{}", ether_type));
+            assert_eq!(str_value, &format!("{:?}", ether_type));
+        }
+    }
+
+    #[test]
+    fn default() {
+        let value: ArpHardwareId = Default::default();
+        assert_eq!(ArpHardwareId(0), value);
+    }
+
+    #[test]
+    fn clone_eq() {
+        let values = &[
+            ArpHardwareId::NETROM,
+            ArpHardwareId::ETHER,
+            ArpHardwareId::EETHER,
+            ArpHardwareId::AX25,
+            ArpHardwareId::PRONET,
+            ArpHardwareId::CHAOS,
+            ArpHardwareId::IEEE802,
+            ArpHardwareId::ARCNET,
+            ArpHardwareId::APPLETLK,
+            ArpHardwareId::DLCI,
+            ArpHardwareId::ATM,
+            ArpHardwareId::METRICOM,
+            ArpHardwareId::IEEE1394,
+            ArpHardwareId::EUI64,
+            ArpHardwareId::INFINIBAND,
+            ArpHardwareId::SLIP,
+            ArpHardwareId::CSLIP,
+            ArpHardwareId::SLIP6,
+            ArpHardwareId::CSLIP6,
+            ArpHardwareId::RSRVD,
+            ArpHardwareId::ADAPT,
+            ArpHardwareId::ROSE,
+            ArpHardwareId::X25,
+            ArpHardwareId::HWX25,
+            ArpHardwareId::CAN,
+            ArpHardwareId::PPP,
+            ArpHardwareId::CISCO_HDLC,
+            ArpHardwareId::LAPB,
+            ArpHardwareId::DDCMP,
+            ArpHardwareId::RAWHDLC,
+            ArpHardwareId::RAWIP,
+            ArpHardwareId::TUNNEL,
+            ArpHardwareId::TUNNEL6,
+            ArpHardwareId::FRAD,
+            ArpHardwareId::SKIP,
+            ArpHardwareId::LOOPBACK,
+            ArpHardwareId::LOCALTLK,
+            ArpHardwareId::FDDI,
+            ArpHardwareId::BIF,
+            ArpHardwareId::SIT,
+            ArpHardwareId::IPDDP,
+            ArpHardwareId::IPGRE,
+            ArpHardwareId::PIMREG,
+            ArpHardwareId::HIPPI,
+            ArpHardwareId::ASH,
+            ArpHardwareId::ECONET,
+            ArpHardwareId::IRDA,
+            ArpHardwareId::FCPP,
+            ArpHardwareId::FCAL,
+            ArpHardwareId::FCPL,
+            ArpHardwareId::FCFABRIC,
+            ArpHardwareId::IEEE802_TR,
+            ArpHardwareId::IEEE80211,
+            ArpHardwareId::IEEE80211_PRISM,
+            ArpHardwareId::IEEE80211_RADIOTAP,
+            ArpHardwareId::IEEE802154,
+            ArpHardwareId::IEEE802154_MONITOR,
+            ArpHardwareId::PHONET,
+            ArpHardwareId::PHONET_PIPE,
+            ArpHardwareId::CAIF,
+            ArpHardwareId::IP6GRE,
+            ArpHardwareId::NETLINK,
+            ArpHardwareId::IPV6LOWPAN,
+            ArpHardwareId::VSOCKMON,
+            ArpHardwareId::VOID,
+            ArpHardwareId::NONE,
+        ];
+
+        // clone
+        for v in values {
+            assert_eq!(v, &v.clone());
+        }
+
+        // eq
+        for (a_pos, a) in values.iter().enumerate() {
+            for (b_pos, b) in values.iter().enumerate() {
+                assert_eq!(a_pos == b_pos, a == b);
+                assert_eq!(a_pos != b_pos, a != b);
+            }
+        }
+    }
+}
diff --git a/src/link/double_vlan_header.rs b/src/link/double_vlan_header.rs
new file mode 100644
index 0000000..dd2aed1
--- /dev/null
+++ b/src/link/double_vlan_header.rs
@@ -0,0 +1,308 @@
+use crate::*;
+
+/// IEEE 802.1Q double VLAN Tagging Header
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct DoubleVlanHeader {
+    /// The outer vlan tagging header
+    pub outer: SingleVlanHeader,
+    /// The inner vlan tagging header
+    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<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut T,
+    ) -> Result<DoubleVlanHeader, err::double_vlan::HeaderReadError> {
+        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<T: std::io::Write + Sized>(&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 {
+            outer: SingleVlanHeader {
+                pcp: VlanPcp::ZERO,
+                drop_eligible_indicator: false,
+                vlan_id: Default::default(),
+                ether_type: ether_type::VLAN_TAGGED_FRAME,
+            },
+            inner: Default::default(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{test_gens::*, *};
+    use alloc::{format, vec::Vec};
+    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::<u8>(), 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<u8> = 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::<u8>(), 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<u8> = 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<u8> = 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() {
+        let actual: DoubleVlanHeader = Default::default();
+        assert_eq!(actual.outer, {
+            let mut outer: SingleVlanHeader = Default::default();
+            outer.ether_type = ether_type::VLAN_TAGGED_FRAME;
+            outer
+        });
+        assert_eq!(actual.inner, Default::default());
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(input in vlan_double_any()) {
+            assert_eq!(input, input.clone());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn dbg(input in vlan_double_any()) {
+            assert_eq!(
+                &format!(
+                    "DoubleVlanHeader {{ outer: {:?}, inner: {:?} }}",
+                    input.outer,
+                    input.inner,
+                ),
+                &format!("{:?}", input)
+            );
+        }
+    }
+}
diff --git a/src/link/double_vlan_header_slice.rs b/src/link/double_vlan_header_slice.rs
new file mode 100644
index 0000000..38a4945
--- /dev/null
+++ b/src/link/double_vlan_header_slice.rs
@@ -0,0 +1,204 @@
+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<DoubleVlanHeaderSlice<'a>, 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::<u8>(), 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<u8> = 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/src/link/double_vlan_slice.rs b/src/link/double_vlan_slice.rs
new file mode 100644
index 0000000..ead1c36
--- /dev/null
+++ b/src/link/double_vlan_slice.rs
@@ -0,0 +1,244 @@
+use crate::{err::*, *};
+
+/// Slice containing a VLAN header & payload.
+#[derive(Clone, Eq, PartialEq)]
+pub struct DoubleVlanSlice<'a> {
+    pub(crate) slice: &'a [u8],
+}
+
+impl<'a> DoubleVlanSlice<'a> {
+    /// Try creating a [`DoubleVlanSlice`] from a slice containing the
+    /// VLAN header & payload.
+    pub fn from_slice(
+        slice: &'a [u8],
+    ) -> Result<DoubleVlanSlice<'a>, 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(),
+        }
+    }
+
+    /// Returns the slice containing the payload & ether type
+    /// 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(),
+        }
+    }
+
+    /// 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
+    }
+}
+
+impl<'a> core::fmt::Debug for DoubleVlanSlice<'a> {
+    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("payload", &self.payload())
+            .finish()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn debug_clone_eq(
+            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() +
+                payload.len()
+            );
+            data.extend_from_slice(&vlan.to_bytes());
+            data.extend_from_slice(&payload);
+
+            // decode packet
+            let slice = DoubleVlanSlice::from_slice(&data).unwrap();
+
+            // check debug output
+            prop_assert_eq!(
+                format!("{:?}", slice),
+                format!(
+                    "DoubleVlanSlice {{ outer: {:?}, inner: {:?}, payload: {:?} }}",
+                    slice.outer().to_header(),
+                    slice.inner().to_header(),
+                    slice.payload(),
+                )
+            );
+            prop_assert_eq!(slice.clone(), slice);
+        }
+    }
+
+    proptest! {
+        #[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() +
+                payload.len()
+            );
+            data.extend_from_slice(&vlan.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());
+            assert_eq!(vlan, slice.to_header());
+            assert_eq!(
+                EtherPayloadSlice{
+                    ether_type: vlan.inner.ether_type,
+                    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/src/link/ether_payload_slice.rs b/src/link/ether_payload_slice.rs
new file mode 100644
index 0000000..0a19e81
--- /dev/null
+++ b/src/link/ether_payload_slice.rs
@@ -0,0 +1,60 @@
+use crate::*;
+
+/// Payload of an IP packet.
+#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
+pub struct EtherPayloadSlice<'a> {
+    /// Identifying content of the payload.
+    pub ether_type: EtherType,
+
+    /// Payload
+    pub payload: &'a [u8],
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        let s = EtherPayloadSlice {
+            ether_type: EtherType::IPV4,
+            payload: &[],
+        };
+        assert_eq!(
+            format!(
+                "EtherPayloadSlice {{ ether_type: {:?}, payload: {:?} }}",
+                s.ether_type, s.payload
+            ),
+            format!("{:?}", s)
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash_ord() {
+        let s = EtherPayloadSlice {
+            ether_type: EtherType::IPV4,
+            payload: &[],
+        };
+        assert_eq!(s.clone(), s);
+
+        use std::collections::hash_map::DefaultHasher;
+        use std::hash::{Hash, Hasher};
+
+        let a_hash = {
+            let mut hasher = DefaultHasher::new();
+            s.hash(&mut hasher);
+            hasher.finish()
+        };
+        let b_hash = {
+            let mut hasher = DefaultHasher::new();
+            s.clone().hash(&mut hasher);
+            hasher.finish()
+        };
+        assert_eq!(a_hash, b_hash);
+
+        use std::cmp::Ordering;
+        assert_eq!(s.clone().cmp(&s), Ordering::Equal);
+        assert_eq!(s.clone().partial_cmp(&s), Some(Ordering::Equal));
+    }
+}
diff --git a/src/link/ether_type_impl.rs b/src/link/ether_type_impl.rs
new file mode 100644
index 0000000..9cf6518
--- /dev/null
+++ b/src/link/ether_type_impl.rs
@@ -0,0 +1,251 @@
+/// Represents an "Ethertype" 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`:
+///
+/// ```
+/// use etherparse::EtherType;
+///
+/// assert_eq!(EtherType::IPV4.0, 0x0800);
+/// assert_eq!(EtherType::IPV4, EtherType(0x0800));
+///
+/// // convert to EtherType using the from & into trait
+/// let ether_type: EtherType = 0x0800.into();
+/// assert_eq!(EtherType::IPV4, ether_type);
+///
+/// // convert to u16 using the from & into trait
+/// let num: u16 = EtherType::IPV4.into();
+/// assert_eq!(0x0800, num);
+/// ```
+///
+/// The constants are also defined in the `ether_type` module so they can
+/// be used without the need to write `EtherType::` in front of them:
+///
+/// ```
+/// use etherparse::{ether_type::IPV4, EtherType};
+///
+/// assert_eq!(IPV4, EtherType::IPV4);
+/// ```
+///
+#[derive(Default, PartialEq, Eq, Clone, Copy, Hash, Ord, PartialOrd)]
+pub struct EtherType(pub u16);
+
+impl EtherType {
+    pub const IPV4: EtherType = Self(0x0800);
+    pub const IPV6: EtherType = Self(0x86dd);
+    pub const ARP: EtherType = Self(0x0806);
+    pub const WAKE_ON_LAN: EtherType = Self(0x0842);
+    pub const VLAN_TAGGED_FRAME: EtherType = Self(0x8100);
+    pub const PROVIDER_BRIDGING: EtherType = Self(0x88A8);
+    pub const VLAN_DOUBLE_TAGGED_FRAME: EtherType = Self(0x9100);
+}
+
+impl From<u16> for EtherType {
+    #[inline]
+    fn from(val: u16) -> Self {
+        EtherType(val)
+    }
+}
+
+impl From<EtherType> for u16 {
+    #[inline]
+    fn from(val: EtherType) -> Self {
+        val.0
+    }
+}
+
+impl core::fmt::Debug for EtherType {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        match *self {
+            Self::IPV4 => write!(f, "{:#06X} (Internet Protocol version 4 (IPv4))", self.0),
+            Self::IPV6 => write!(f, "{:#06X} (Internet Protocol Version 6 (IPV6))", self.0),
+            Self::ARP => write!(f, "{:#06X} (Address Resolution Protocol (ARP))", self.0),
+            Self::WAKE_ON_LAN => write!(f, "{:#06X} (Wake on LAN)", self.0),
+            Self::VLAN_TAGGED_FRAME => write!(
+                f,
+                "{:#06X} (Customer VLAN Tag (C-TAG) as defined in IEEE Std 802.1Q)",
+                self.0
+            ),
+            Self::PROVIDER_BRIDGING => write!(
+                f,
+                "{:#06X} (IEEE Std 802.1Q - Service VLAN tag identifier (S-Tag))",
+                self.0
+            ),
+            Self::VLAN_DOUBLE_TAGGED_FRAME => {
+                write!(f, "{:#06X} (VLAN Double Tagged Frame)", self.0)
+            }
+            _ => write!(f, "{:#06X}", self.0),
+        }
+    }
+}
+
+/// Constants for the ethertype values for easy importing (e.g. `use ether_type::*;`).
+///
+/// The constants only exist for convenience so you can import them
+/// (`use ether_type::*`) without a need to write `EtherType::` in front
+/// of every constant.
+///
+/// You can access the underlying `u16` value by using `.0` and any `u16`
+/// can be converted to an `EtherType`:
+///
+/// ```
+/// use etherparse::{ether_type::IPV4, EtherType};
+///
+/// assert_eq!(IPV4.0, 0x0800);
+/// assert_eq!(IPV4, EtherType(0x0800));
+/// let num: EtherType = 0x0800.into();
+/// assert_eq!(IPV4, num);
+/// ```
+pub mod ether_type {
+    use super::EtherType;
+
+    pub const IPV4: EtherType = EtherType::IPV4;
+    pub const IPV6: EtherType = EtherType::IPV6;
+    pub const ARP: EtherType = EtherType::ARP;
+    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;
+    pub const VLAN_DOUBLE_TAGGED_FRAME: EtherType = EtherType::VLAN_DOUBLE_TAGGED_FRAME;
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{ether_type, EtherType};
+    use alloc::format;
+
+    #[test]
+    fn to_u16() {
+        assert_eq!(0x0800, u16::from(EtherType::IPV4));
+        assert_eq!(0x86dd, u16::from(EtherType::IPV6));
+        assert_eq!(0x0806, u16::from(EtherType::ARP));
+        assert_eq!(0x0842, u16::from(EtherType::WAKE_ON_LAN));
+        assert_eq!(0x8100, u16::from(EtherType::VLAN_TAGGED_FRAME));
+        assert_eq!(0x88A8, u16::from(EtherType::PROVIDER_BRIDGING));
+        assert_eq!(0x9100, u16::from(EtherType::VLAN_DOUBLE_TAGGED_FRAME));
+    }
+
+    #[test]
+    fn from_u16() {
+        assert_eq!(EtherType::from(0x0800), EtherType::IPV4);
+        assert_eq!(EtherType::from(0x86dd), EtherType::IPV6);
+        assert_eq!(EtherType::from(0x0806), EtherType::ARP);
+        assert_eq!(EtherType::from(0x0842), EtherType::WAKE_ON_LAN);
+        assert_eq!(EtherType::from(0x8100), EtherType::VLAN_TAGGED_FRAME);
+        assert_eq!(EtherType::from(0x88A8), EtherType::PROVIDER_BRIDGING);
+        assert_eq!(EtherType::from(0x9100), EtherType::VLAN_DOUBLE_TAGGED_FRAME);
+        assert_eq!(EtherType::from(0x1234), EtherType(0x1234));
+    }
+
+    #[test]
+    fn constants() {
+        use ether_type::*;
+        let pairs = &[
+            (EtherType::IPV4, IPV4),
+            (EtherType::IPV6, IPV6),
+            (EtherType::ARP, ARP),
+            (EtherType::WAKE_ON_LAN, WAKE_ON_LAN),
+            (EtherType::VLAN_TAGGED_FRAME, VLAN_TAGGED_FRAME),
+            (EtherType::PROVIDER_BRIDGING, PROVIDER_BRIDGING),
+            (
+                EtherType::VLAN_DOUBLE_TAGGED_FRAME,
+                VLAN_DOUBLE_TAGGED_FRAME,
+            ),
+        ];
+
+        for (ether_type, constant) in pairs {
+            assert_eq!(ether_type, constant);
+        }
+    }
+
+    #[test]
+    fn dbg() {
+        let pairs = &[
+            (
+                EtherType::IPV4,
+                "0x0800 (Internet Protocol version 4 (IPv4))",
+            ),
+            (
+                EtherType::IPV6,
+                "0x86DD (Internet Protocol Version 6 (IPV6))",
+            ),
+            (EtherType::ARP, "0x0806 (Address Resolution Protocol (ARP))"),
+            (EtherType::WAKE_ON_LAN, "0x0842 (Wake on LAN)"),
+            (
+                EtherType::VLAN_TAGGED_FRAME,
+                "0x8100 (Customer VLAN Tag (C-TAG) as defined in IEEE Std 802.1Q)",
+            ),
+            (
+                EtherType::PROVIDER_BRIDGING,
+                "0x88A8 (IEEE Std 802.1Q - Service VLAN tag identifier (S-Tag))",
+            ),
+            (
+                EtherType::VLAN_DOUBLE_TAGGED_FRAME,
+                "0x9100 (VLAN Double Tagged Frame)",
+            ),
+            (EtherType(1), "0x0001"),
+        ];
+
+        for (ether_type, str_value) in pairs {
+            assert_eq!(str_value, &format!("{:?}", ether_type));
+        }
+    }
+
+    #[test]
+    fn default() {
+        let value: EtherType = Default::default();
+        assert_eq!(EtherType(0), value);
+    }
+
+    #[test]
+    fn clone_eq() {
+        let values = &[
+            EtherType::IPV4,
+            EtherType::IPV6,
+            EtherType::ARP,
+            EtherType::WAKE_ON_LAN,
+            EtherType::VLAN_TAGGED_FRAME,
+            EtherType::PROVIDER_BRIDGING,
+            EtherType::VLAN_DOUBLE_TAGGED_FRAME,
+        ];
+
+        // clone
+        for v in values {
+            assert_eq!(v, &v.clone());
+        }
+
+        // eq
+        for (a_pos, a) in values.iter().enumerate() {
+            for (b_pos, b) in values.iter().enumerate() {
+                assert_eq!(a_pos == b_pos, a == b);
+                assert_eq!(a_pos != b_pos, a != b);
+            }
+        }
+    }
+
+    #[test]
+    fn hash_ord() {
+        use core::cmp::Ordering;
+        use core::hash::{Hash, Hasher};
+        use std::collections::hash_map::DefaultHasher;
+
+        // hash
+        let a_hash = {
+            let mut s = DefaultHasher::new();
+            EtherType::IPV4.hash(&mut s);
+            s.finish()
+        };
+        let b_hash = {
+            let mut s = DefaultHasher::new();
+            EtherType::IPV4.hash(&mut s);
+            s.finish()
+        };
+        assert_eq!(a_hash, b_hash);
+
+        // order
+        assert_eq!(
+            EtherType::IPV4.cmp(&EtherType::IPV4.clone()),
+            Ordering::Equal
+        );
+        assert!(EtherType::IPV4.ge(&EtherType::IPV4.clone()));
+    }
+}
diff --git a/src/link/ethernet2_header.rs b/src/link/ethernet2_header.rs
new file mode 100644
index 0000000..6334068
--- /dev/null
+++ b/src/link/ethernet2_header.rs
@@ -0,0 +1,319 @@
+use crate::{err::Layer, err::SliceWriteSpaceError, *};
+
+/// Ethernet II header.
+#[derive(Clone, Debug, Eq, PartialEq, Default)]
+pub struct Ethernet2Header {
+    /// Source MAC Address
+    pub source: [u8; 6],
+    /// Destination MAC Address
+    pub destination: [u8; 6],
+    /// Protocol present after the ethernet2 header.
+    pub ether_type: EtherType,
+}
+
+impl Ethernet2Header {
+    /// Serialized size of an Ethernet2 header in bytes/octets.
+    pub const LEN: usize = 14;
+
+    /// Deprecated use [`Ethernet2Header::LEN`] instead.
+    #[deprecated(since = "0.14.0", note = "Use `Ethernet2Header::LEN` instead")]
+    pub const SERIALIZED_SIZE: usize = Ethernet2Header::LEN;
+
+    /// Deprecated use [`Ethernet2Header::from_slice`] instead.
+    #[deprecated(since = "0.10.1", note = "Use Ethernet2Header::from_slice instead.")]
+    #[inline]
+    pub fn read_from_slice(slice: &[u8]) -> Result<(Ethernet2Header, &[u8]), err::LenError> {
+        Ethernet2Header::from_slice(slice)
+    }
+
+    /// Read an Ethernet2Header from a slice and return the header & unused parts of the slice.
+    #[inline]
+    pub fn from_slice(slice: &[u8]) -> Result<(Ethernet2Header, &[u8]), err::LenError> {
+        Ok((
+            Ethernet2HeaderSlice::from_slice(slice)?.to_header(),
+            &slice[Ethernet2Header::LEN..],
+        ))
+    }
+
+    /// Read an Ethernet2Header from a static sized byte array.
+    #[inline]
+    pub fn from_bytes(bytes: [u8; 14]) -> Ethernet2Header {
+        Ethernet2Header {
+            destination: [bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]],
+            source: [bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11]],
+            ether_type: EtherType(u16::from_be_bytes([bytes[12], bytes[13]])),
+        }
+    }
+
+    /// Reads an Ethernet-II header from the current position of the read argument.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut T,
+    ) -> Result<Ethernet2Header, std::io::Error> {
+        let buffer = {
+            let mut buffer = [0; Ethernet2Header::LEN];
+            reader.read_exact(&mut buffer)?;
+            buffer
+        };
+
+        Ok(
+            // SAFETY: Safe as the buffer contains exactly the needed Ethernet2Header::LEN bytes.
+            unsafe { Ethernet2HeaderSlice::from_slice_unchecked(&buffer) }.to_header(),
+        )
+    }
+
+    /// Serialize the header to a given slice. Returns the unused part of the slice.
+    pub fn write_to_slice<'a>(
+        &self,
+        slice: &'a mut [u8],
+    ) -> Result<&'a mut [u8], SliceWriteSpaceError> {
+        // length check
+        if slice.len() < Ethernet2Header::LEN {
+            Err(SliceWriteSpaceError {
+                required_len: Ethernet2Header::LEN,
+                len: slice.len(),
+                layer: Layer::Ethernet2Header,
+                layer_start_offset: 0,
+            })
+        } else {
+            slice[..Ethernet2Header::LEN].copy_from_slice(&self.to_bytes());
+            Ok(&mut slice[Ethernet2Header::LEN..])
+        }
+    }
+
+    /// Writes a given Ethernet-II header to the current position of the write argument.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    #[inline]
+    pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
+        writer.write_all(&self.to_bytes())
+    }
+
+    /// Length of the serialized header in bytes.
+    #[inline]
+    pub fn header_len(&self) -> usize {
+        14
+    }
+
+    /// Returns the serialized form of the header as a statically
+    /// sized byte array.
+    #[inline]
+    pub fn to_bytes(&self) -> [u8; 14] {
+        let ether_type_be = self.ether_type.0.to_be_bytes();
+        [
+            self.destination[0],
+            self.destination[1],
+            self.destination[2],
+            self.destination[3],
+            self.destination[4],
+            self.destination[5],
+            self.source[0],
+            self.source[1],
+            self.source[2],
+            self.source[3],
+            self.source[4],
+            self.source[5],
+            ether_type_be[0],
+            ether_type_be[1],
+        ]
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+    use std::io::{Cursor, ErrorKind};
+
+    #[test]
+    fn default() {
+        let e: Ethernet2Header = Default::default();
+        assert_eq!([0u8; 6], e.source);
+        assert_eq!([0u8; 6], e.destination);
+        assert_eq!(EtherType(0), e.ether_type);
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            input in ethernet_2_any(),
+            dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
+        ) {
+            // serialize
+            let mut buffer: Vec<u8> = Vec::with_capacity(14 + dummy_data.len());
+            input.write(&mut buffer).unwrap();
+            buffer.extend(&dummy_data[..]);
+
+            // calls with a valid result
+            {
+                let (result, rest) = Ethernet2Header::from_slice(&buffer[..]).unwrap();
+                assert_eq!(input, result);
+                assert_eq!(&buffer[14..], rest);
+            }
+            #[allow(deprecated)]
+            {
+                let (result, rest) = Ethernet2Header::read_from_slice(&buffer[..]).unwrap();
+                assert_eq!(input, result);
+                assert_eq!(&buffer[14..], rest);
+            }
+
+            // call with not enough data in the slice
+            for len in 0..=13 {
+                assert_eq!(
+                    Ethernet2Header::from_slice(&buffer[..len]),
+                    Err(err::LenError{
+                        required_len: Ethernet2Header::LEN,
+                        len: len,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::Ethernet2Header,
+                        layer_start_offset: 0,
+                    })
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_bytes(input in ethernet_2_any()) {
+            assert_eq!(
+                input,
+                Ethernet2Header::from_bytes(input.to_bytes())
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read(
+            input in ethernet_2_any(),
+            dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
+        ) {
+            // normal read
+            let mut buffer = Vec::with_capacity(14 + dummy_data.len());
+            input.write(&mut buffer).unwrap();
+            buffer.extend(&dummy_data[..]);
+
+            // calls with a valid result
+            {
+                let mut cursor = Cursor::new(&buffer);
+                let result = Ethernet2Header::read(&mut cursor).unwrap();
+                assert_eq!(input, result);
+                assert_eq!(cursor.position(), 14);
+            }
+
+            // unexpected eof
+            for len in 0..=13 {
+                let mut cursor = Cursor::new(&buffer[0..len]);
+                assert_eq!(
+                    Ethernet2Header::read(&mut cursor)
+                    .unwrap_err()
+                    .kind(),
+                    ErrorKind::UnexpectedEof
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write_to_slice(input in ethernet_2_any()) {
+            // normal write
+            {
+                let mut buffer: [u8;14] = [0;14];
+                input.write_to_slice(&mut buffer).unwrap();
+                assert_eq!(buffer, input.to_bytes());
+            }
+            // len to small
+            for len in 0..14 {
+                let mut buffer: [u8;14] = [0;14];
+                assert_eq!(
+                    SliceWriteSpaceError {
+                        required_len: Ethernet2Header::LEN,
+                        len,
+                        layer: Layer::Ethernet2Header,
+                        layer_start_offset: 0,
+                    },
+                    input.write_to_slice(&mut buffer[..len]).unwrap_err()
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write(input in ethernet_2_any()) {
+            // successful write
+            {
+                let mut buffer: Vec<u8> = Vec::with_capacity(14);
+                input.write(&mut buffer).unwrap();
+                assert_eq!(&buffer[..], &input.to_bytes());
+            }
+
+            // not enough memory for write (unexpected eof)
+            for len in 0..8 {
+                let mut buffer = [0u8;8];
+                let mut writer = Cursor::new(&mut buffer[..len]);
+                assert!(input.write(&mut writer).is_err());
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header_len(input in ethernet_2_any()) {
+            assert_eq!(input.header_len(), 14);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_bytes(input in ethernet_2_any()) {
+            let ether_type_be = input.ether_type.0.to_be_bytes();
+            assert_eq!(
+                input.to_bytes(),
+                [
+                    input.destination[0],
+                    input.destination[1],
+                    input.destination[2],
+                    input.destination[3],
+                    input.destination[4],
+                    input.destination[5],
+                    input.source[0],
+                    input.source[1],
+                    input.source[2],
+                    input.source[3],
+                    input.source[4],
+                    input.source[5],
+                    ether_type_be[0],
+                    ether_type_be[1],
+                ]
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(input in ethernet_2_any()) {
+            assert_eq!(input, input.clone());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn dbg(input in ethernet_2_any()) {
+            assert_eq!(
+                &format!(
+                    "Ethernet2Header {{ source: {:?}, destination: {:?}, ether_type: {:?} }}",
+                    input.source,
+                    input.destination,
+                    input.ether_type
+                ),
+                &format!("{:?}", input)
+            );
+        }
+    }
+}
diff --git a/src/link/ethernet2_header_slice.rs b/src/link/ethernet2_header_slice.rs
new file mode 100644
index 0000000..17d5191
--- /dev/null
+++ b/src/link/ethernet2_header_slice.rs
@@ -0,0 +1,178 @@
+use crate::*;
+use core::slice::from_raw_parts;
+
+///A slice containing an ethernet 2 header of a network package.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Ethernet2HeaderSlice<'a> {
+    pub(crate) slice: &'a [u8],
+}
+
+impl<'a> Ethernet2HeaderSlice<'a> {
+    /// Creates a ethernet slice from an other slice.
+    pub fn from_slice(slice: &'a [u8]) -> Result<Ethernet2HeaderSlice<'a>, err::LenError> {
+        //check length
+        if slice.len() < Ethernet2Header::LEN {
+            return Err(err::LenError {
+                required_len: Ethernet2Header::LEN,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::Ethernet2Header,
+                layer_start_offset: 0,
+            });
+        }
+
+        //all done
+        Ok(Ethernet2HeaderSlice {
+            // SAFETY:
+            // Safe as slice length is checked to be at least
+            // Ethernet2Header::LEN (14) before this.
+            slice: unsafe { from_raw_parts(slice.as_ptr(), Ethernet2Header::LEN) },
+        })
+    }
+
+    /// Converts the given slice into a ethernet 2 header slice WITHOUT any
+    /// checks to ensure that the data present is an ethernet 2 header or that the
+    /// slice length is matching the header length.
+    ///
+    /// If you are not sure what this means, use [`Ethernet2HeaderSlice::from_slice`]
+    /// instead.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensured that the given slice has the length of
+    /// [`Ethernet2Header::LEN`]
+    #[inline]
+    #[cfg(feature = "std")]
+    pub(crate) unsafe fn from_slice_unchecked(slice: &[u8]) -> Ethernet2HeaderSlice {
+        debug_assert!(slice.len() == Ethernet2Header::LEN);
+        Ethernet2HeaderSlice { slice }
+    }
+
+    /// Returns the slice containing the ethernet 2 header
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+
+    /// Read the destination MAC address
+    #[inline]
+    pub fn destination(&self) -> [u8; 6] {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of Ethernet2Header::LEN (14).
+        unsafe { get_unchecked_6_byte_array(self.slice.as_ptr()) }
+    }
+
+    /// Read the source MAC address
+    #[inline]
+    pub fn source(&self) -> [u8; 6] {
+        // SAFETY:
+        // Safe as the contructor 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)) }
+    }
+
+    /// Read the ether_type field of the header indicating the protocol
+    /// after the header.
+    #[inline]
+    pub fn ether_type(&self) -> EtherType {
+        // SAFETY:
+        // Safe as the contructor 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)) })
+    }
+
+    /// Decode all the fields and copy the results to a [`Ethernet2Header`] struct
+    pub fn to_header(&self) -> Ethernet2Header {
+        Ethernet2Header {
+            source: self.source(),
+            destination: self.destination(),
+            ether_type: self.ether_type(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            input in ethernet_2_any(),
+            dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
+        ) {
+            // serialize
+            let mut buffer: Vec<u8> = Vec::with_capacity(14 + dummy_data.len());
+            input.write(&mut buffer).unwrap();
+            buffer.extend(&dummy_data[..]);
+
+            // calls with a valid result
+            {
+                let result = Ethernet2HeaderSlice::from_slice(&buffer[..]).unwrap();
+                assert_eq!(&buffer[..14], result.slice());
+            }
+
+            // call with not enough data in the slice
+            for len in 0..=13 {
+                assert_eq!(
+                    Ethernet2HeaderSlice::from_slice(&buffer[..len]),
+                    Err(err::LenError{
+                        required_len: Ethernet2Header::LEN,
+                        len: len,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::Ethernet2Header,
+                        layer_start_offset: 0,
+                    })
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn getters(input in ethernet_2_any()) {
+            let buffer = input.to_bytes();
+            let slice = Ethernet2HeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(input.destination, slice.destination());
+            assert_eq!(input.source, slice.source());
+            assert_eq!(input.ether_type, slice.ether_type());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_header(input in ethernet_2_any()) {
+            let buffer = input.to_bytes();
+            let slice = Ethernet2HeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(input, slice.to_header());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(input in ethernet_2_any()) {
+            let buffer = input.to_bytes();
+            let slice = Ethernet2HeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(slice, slice.clone());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn dbg(input in ethernet_2_any()) {
+            let buffer = input.to_bytes();
+            let slice = Ethernet2HeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(
+                &format!(
+                    "Ethernet2HeaderSlice {{ slice: {:?} }}",
+                    slice.slice()
+                ),
+                &format!("{:?}", slice)
+            );
+        }
+    }
+}
diff --git a/src/link/ethernet2_slice.rs b/src/link/ethernet2_slice.rs
new file mode 100644
index 0000000..5460632
--- /dev/null
+++ b/src/link/ethernet2_slice.rs
@@ -0,0 +1,358 @@
+use crate::{err::*, *};
+
+/// Slice containing an Ethernet 2 headers & payload.
+#[derive(Clone, Eq, PartialEq)]
+pub struct Ethernet2Slice<'a> {
+    fcs_len: usize,
+    slice: &'a [u8],
+}
+
+impl<'a> Ethernet2Slice<'a> {
+    /// Try creating a [`Ethernet2Slice`] from a slice containing the
+    /// Ethernet 2 header & payload WITHOUT an FCS (frame check sequence)
+    /// at the end.
+    pub fn from_slice_without_fcs(slice: &'a [u8]) -> Result<Ethernet2Slice<'a>, LenError> {
+        // check length
+        if slice.len() < Ethernet2Header::LEN {
+            return Err(LenError {
+                required_len: Ethernet2Header::LEN,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: Layer::Ethernet2Header,
+                layer_start_offset: 0,
+            });
+        }
+
+        Ok(Ethernet2Slice { fcs_len: 0, slice })
+    }
+
+    /// Try creating a [`Ethernet2Slice`] from a slice containing the
+    /// Ethernet 2 header & payload with a CRC 32 bit FCS (frame
+    /// check sequence) at the end.
+    ///
+    /// In case you are not sure if your ethernet2 frame has a FCS or not
+    /// use [`Ethernet2Slice::from_slice_without_fcs`] instead and rely on the
+    /// lower layers (e.g. IP) to determine the correct payload length.
+    pub fn from_slice_with_crc32_fcs(slice: &'a [u8]) -> Result<Ethernet2Slice<'a>, LenError> {
+        // check length
+        let fcs_len = 4;
+        if slice.len() < Ethernet2Header::LEN + fcs_len {
+            return Err(LenError {
+                required_len: Ethernet2Header::LEN + 4,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: Layer::Ethernet2Header,
+                layer_start_offset: 0,
+            });
+        }
+
+        Ok(Ethernet2Slice { fcs_len, slice })
+    }
+
+    /// Returns the slice containing the ethernet 2 header
+    /// payload and FCS if present.
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+
+    /// Read the destination MAC address
+    #[inline]
+    pub fn destination(&self) -> [u8; 6] {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of Ethernet2Header::LEN (14).
+        unsafe { get_unchecked_6_byte_array(self.slice.as_ptr()) }
+    }
+
+    /// Read the source MAC address
+    #[inline]
+    pub fn source(&self) -> [u8; 6] {
+        // SAFETY:
+        // Safe as the contructor 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)) }
+    }
+
+    /// Read the ether_type field of the header indicating the protocol
+    /// after the header.
+    #[inline]
+    pub fn ether_type(&self) -> EtherType {
+        // SAFETY:
+        // Safe as the contructor 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)) })
+    }
+
+    /// Returns the frame check sequence if present.
+    #[inline]
+    pub fn fcs(&self) -> Option<[u8; 4]> {
+        if self.fcs_len == 4 {
+            // SAFETY: Safe as the slice length was verified
+            // to be at least Ethernet2Header::LEN + fcs_len by
+            // "from_slice_without_fcs" & "from_slice_with_crc32_fcs".
+            Some(unsafe {
+                [
+                    *self.slice.as_ptr().add(self.slice.len() - 4),
+                    *self.slice.as_ptr().add(self.slice.len() - 3),
+                    *self.slice.as_ptr().add(self.slice.len() - 2),
+                    *self.slice.as_ptr().add(self.slice.len() - 1),
+                ]
+            })
+        } else {
+            None
+        }
+    }
+
+    /// Decode all the fields and copy the results to a [`Ethernet2Header`] struct
+    pub fn to_header(&self) -> Ethernet2Header {
+        Ethernet2Header {
+            source: self.source(),
+            destination: self.destination(),
+            ether_type: self.ether_type(),
+        }
+    }
+
+    /// Slice containing the Ethernet 2 header.
+    pub fn header_slice(&self) -> &[u8] {
+        unsafe {
+            // SAFETY:
+            // Safe as the contructor checks that the slice has
+            // at least the length of Ethernet2Header::LEN (14).
+            core::slice::from_raw_parts(self.slice.as_ptr(), Ethernet2Header::LEN)
+        }
+    }
+
+    /// Returns the slice containing the Ethernet II payload & ether type
+    /// identifying it's content type.
+    #[inline]
+    pub fn payload(&self) -> EtherPayloadSlice<'a> {
+        EtherPayloadSlice {
+            ether_type: self.ether_type(),
+            payload: self.payload_slice(),
+        }
+    }
+
+    /// Returns the slice containing the Ethernet II payload.
+    #[inline]
+    pub fn payload_slice(&self) -> &'a [u8] {
+        unsafe {
+            // SAFETY: Safe as the slice length was verified
+            // to be at least Ethernet2Header::LEN + fcs_len by
+            // "from_slice_without_fcs" & "from_slice_with_crc32_fcs".
+            core::slice::from_raw_parts(
+                self.slice.as_ptr().add(Ethernet2Header::LEN),
+                self.slice.len() - Ethernet2Header::LEN - self.fcs_len,
+            )
+        }
+    }
+
+    /// Length of the Ethernet 2 header in bytes (equal to
+    /// [`crate::Ethernet2Header::LEN`]).
+    #[inline]
+    pub const fn header_len(&self) -> usize {
+        Ethernet2Header::LEN
+    }
+}
+
+impl<'a> core::fmt::Debug for Ethernet2Slice<'a> {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        f.debug_struct("Ethernet2Slice")
+            .field("header", &self.to_header())
+            .field("payload", &self.payload())
+            .field("fcs", &self.fcs())
+            .finish()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn debug_clone_eq(
+            eth in ethernet_2_any(),
+            has_fcs in any::<bool>()
+        ) {
+            let payload: [u8;8] = [1,2,3,4,5,6,7,8];
+            let mut data = Vec::with_capacity(
+                eth.header_len() +
+                payload.len()
+            );
+            data.extend_from_slice(&eth.to_bytes());
+            data.extend_from_slice(&payload);
+
+            // decode packet
+            let slice = if has_fcs {
+                Ethernet2Slice::from_slice_with_crc32_fcs(&data).unwrap()
+            } else {
+                Ethernet2Slice::from_slice_without_fcs(&data).unwrap()
+            };
+
+            // check debug output
+            prop_assert_eq!(
+                format!("{:?}", slice),
+                format!(
+                    "Ethernet2Slice {{ header: {:?}, payload: {:?}, fcs: {:?} }}",
+                    slice.to_header(),
+                    slice.payload(),
+                    slice.fcs()
+                )
+            );
+            prop_assert_eq!(slice.clone(), slice);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn getters(eth in ethernet_2_any()) {
+            let payload: [u8;8] = [1,2,3,4,5,6,7,8];
+            let mut data = Vec::with_capacity(
+                eth.header_len() +
+                payload.len()
+            );
+            data.extend_from_slice(&eth.to_bytes());
+            data.extend_from_slice(&payload);
+
+            // without fcs
+            {
+                let slice = Ethernet2Slice::from_slice_without_fcs(&data).unwrap();
+                assert_eq!(eth.destination, slice.destination());
+                assert_eq!(eth.source, slice.source());
+                assert_eq!(eth.ether_type, slice.ether_type());
+                assert_eq!(&payload, slice.payload_slice());
+                assert_eq!(
+                    EtherPayloadSlice{
+                        payload: &payload,
+                        ether_type: eth.ether_type,
+                    },
+                    slice.payload()
+                );
+                assert_eq!(None, slice.fcs());
+                assert_eq!(eth, slice.to_header());
+                assert_eq!(&data, slice.slice());
+                assert_eq!(&data[..Ethernet2Header::LEN], slice.header_slice());
+            }
+            // with fcs
+            {
+                let slice = Ethernet2Slice::from_slice_with_crc32_fcs(&data).unwrap();
+                assert_eq!(eth.destination, slice.destination());
+                assert_eq!(eth.source, slice.source());
+                assert_eq!(eth.ether_type, slice.ether_type());
+                assert_eq!(&payload[..payload.len() - 4], slice.payload_slice());
+                assert_eq!(
+                    EtherPayloadSlice{
+                        payload: &payload[..payload.len() - 4],
+                        ether_type: eth.ether_type,
+                    },
+                    slice.payload()
+                );
+                assert_eq!(Some([5, 6, 7, 8]), slice.fcs());
+                assert_eq!(eth, slice.to_header());
+                assert_eq!(&data, slice.slice());
+                assert_eq!(&data[..Ethernet2Header::LEN], slice.header_slice());
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice_without_fcs(eth in ethernet_2_any()) {
+
+            let payload: [u8;10] = [1,2,3,4,5,6,7,8,9,10];
+            let data = {
+                let mut data = Vec::with_capacity(
+                    eth.header_len() +
+                    payload.len()
+                );
+                data.extend_from_slice(&eth.to_bytes());
+                data.extend_from_slice(&payload);
+                data
+            };
+
+            // normal decode
+            {
+                let slice = Ethernet2Slice::from_slice_without_fcs(&data).unwrap();
+                assert_eq!(slice.to_header(), eth);
+                assert_eq!(slice.payload_slice(), &payload);
+                assert_eq!(slice.fcs(), None);
+            }
+
+            // decode without payload
+            {
+                let slice = Ethernet2Slice::from_slice_without_fcs(&data[..Ethernet2Header::LEN]).unwrap();
+                assert_eq!(slice.to_header(), eth);
+                assert_eq!(slice.payload_slice(), &[]);
+                assert_eq!(slice.fcs(), None);
+            }
+
+            // length error
+            for len in 0..Ethernet2Header::LEN {
+                assert_eq!(
+                    Ethernet2Slice::from_slice_without_fcs(&data[..len]).unwrap_err(),
+                    LenError{
+                        required_len: Ethernet2Header::LEN,
+                        len,
+                        len_source: LenSource::Slice,
+                        layer: Layer::Ethernet2Header,
+                        layer_start_offset: 0
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice_with_crc32_fcs(
+            eth in ethernet_2_any()
+        ) {
+            let payload: [u8;10] = [1,2,3,4,5,6,7,8,9,10];
+            let fcs: [u8;4] = [11,12,13,14];
+            let data = {
+                let mut data = Vec::with_capacity(
+                    eth.header_len() +
+                    payload.len()
+                );
+                data.extend_from_slice(&eth.to_bytes());
+                data.extend_from_slice(&payload);
+                data.extend_from_slice(&fcs);
+                data
+            };
+
+            // normal decode
+            {
+                let slice = Ethernet2Slice::from_slice_with_crc32_fcs(&data).unwrap();
+                assert_eq!(slice.to_header(), eth);
+                assert_eq!(slice.payload_slice(), &payload);
+                assert_eq!(slice.fcs(), Some(fcs));
+            }
+
+            // decode without payload
+            {
+                let slice = Ethernet2Slice::from_slice_with_crc32_fcs(&data[..Ethernet2Header::LEN + 4]).unwrap();
+                assert_eq!(slice.to_header(), eth);
+                assert_eq!(slice.payload_slice(), &[]);
+                assert_eq!(slice.fcs(), Some([1,2,3,4]));
+            }
+
+            // length error
+            for len in 0..Ethernet2Header::LEN + 4 {
+                assert_eq!(
+                    Ethernet2Slice::from_slice_with_crc32_fcs(&data[..len]).unwrap_err(),
+                    LenError{
+                        required_len: Ethernet2Header::LEN + 4,
+                        len,
+                        len_source: LenSource::Slice,
+                        layer: Layer::Ethernet2Header,
+                        layer_start_offset: 0
+                    }
+                );
+            }
+        }
+    }
+}
diff --git a/src/link/link_header.rs b/src/link/link_header.rs
new file mode 100644
index 0000000..1c5caa2
--- /dev/null
+++ b/src/link/link_header.rs
@@ -0,0 +1,238 @@
+use crate::{Ethernet2Header, LinuxSllHeader};
+
+/// The possible headers on the link layer
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum LinkHeader {
+    LinuxSll(LinuxSllHeader),
+    Ethernet2(Ethernet2Header),
+}
+
+impl LinkHeader {
+    /// Returns `Option::Some` containing the `Ethernet2Header` if self has the
+    /// value Ethernet2. Otherwise `Option::None` is returned.
+    pub fn ethernet2(self) -> Option<Ethernet2Header> {
+        use crate::LinkHeader::*;
+        if let Ethernet2(value) = self {
+            Some(value)
+        } else {
+            None
+        }
+    }
+
+    /// Returns `Option::Some` containing the `Ethernet2Header` if self has the
+    /// value Ethernet2. Otherwise `Option::None` is returned.
+    pub fn mut_ethernet2(&mut self) -> Option<&mut Ethernet2Header> {
+        use crate::LinkHeader::*;
+        if let Ethernet2(value) = self {
+            Some(value)
+        } else {
+            None
+        }
+    }
+
+    /// Returns `Option::Some` containing the `LinuxSllHeader` if self has the
+    /// value LinuxSll. Otherwise `Option::None` is returned.
+    pub fn linux_sll(self) -> Option<LinuxSllHeader> {
+        use crate::LinkHeader::*;
+        if let LinuxSll(value) = self {
+            Some(value)
+        } else {
+            None
+        }
+    }
+
+    /// Returns `Option::Some` containing the `LinuxSllHeader` if self has the
+    /// value LinuxSll. Otherwise `Option::None` is returned.
+    pub fn mut_linux_sll(&mut self) -> Option<&mut LinuxSllHeader> {
+        use crate::LinkHeader::*;
+        if let LinuxSll(value) = self {
+            Some(value)
+        } else {
+            None
+        }
+    }
+
+    /// Returns the size of the link header
+    pub fn header_len(&self) -> usize {
+        use crate::LinkHeader::*;
+        match self {
+            Ethernet2(_) => Ethernet2Header::LEN,
+            LinuxSll(_) => LinuxSllHeader::LEN,
+        }
+    }
+
+    /// Write the link header to the given writer.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
+        use crate::LinkHeader::*;
+        match self {
+            Ethernet2(value) => value.write(writer),
+            LinuxSll(value) => value.write(writer),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::{test_gens::*, *};
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+    use std::io::Cursor;
+
+    proptest! {
+        #[test]
+        fn debug(
+            ethernet2 in ethernet_2_any(),
+            linux_sll in linux_sll_any(),
+        ) {
+            assert_eq!(
+                format!("Ethernet2({:?})", ethernet2),
+                format!("{:?}", LinkHeader::Ethernet2(ethernet2.clone())),
+            );
+            assert_eq!(
+                format!("LinuxSll({:?})", linux_sll),
+                format!("{:?}", LinkHeader::LinuxSll(linux_sll.clone())),
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(
+            ethernet2 in ethernet_2_any(),
+            linux_sll in linux_sll_any(),
+        ) {
+            let values = [
+                LinkHeader::Ethernet2(ethernet2),
+                LinkHeader::LinuxSll(linux_sll),
+            ];
+            for value in values {
+                assert_eq!(value.clone(), value);
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn ethernet2(
+            ethernet2 in ethernet_2_any(),
+            linux_sll in linux_sll_any()
+        ) {
+            assert_eq!(Some(ethernet2.clone()), LinkHeader::Ethernet2(ethernet2).ethernet2());
+            assert_eq!(None, LinkHeader::LinuxSll(linux_sll).ethernet2());
+        }
+
+    }
+    proptest! {
+        #[test]
+        fn mut_ethernet2(
+            ethernet2 in ethernet_2_any(),
+            linux_sll in linux_sll_any()
+        ) {
+            assert_eq!(Some(&mut ethernet2.clone()), LinkHeader::Ethernet2(ethernet2).mut_ethernet2());
+            assert_eq!(None, LinkHeader::LinuxSll(linux_sll).mut_ethernet2());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn linux_sll(
+            ethernet2 in ethernet_2_any(),
+            linux_sll in linux_sll_any()
+        ) {
+            assert_eq!(Some(linux_sll.clone()), LinkHeader::LinuxSll(linux_sll).linux_sll());
+            assert_eq!(None, LinkHeader::Ethernet2(ethernet2).linux_sll());
+        }
+
+    }
+    proptest! {
+        #[test]
+        fn mut_linux_sll(
+            ethernet2 in ethernet_2_any(),
+            linux_sll in linux_sll_any()
+        ) {
+            assert_eq!(Some(&mut linux_sll.clone()), LinkHeader::LinuxSll(linux_sll).mut_linux_sll());
+            assert_eq!(None, LinkHeader::Ethernet2(ethernet2).mut_linux_sll());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header_size(
+            ethernet2 in ethernet_2_any(),
+            linux_sll in linux_sll_any()
+        ) {
+            assert_eq!(
+                LinkHeader::Ethernet2(ethernet2).header_len(),
+                Ethernet2Header::LEN
+            );
+            assert_eq!(
+                LinkHeader::LinuxSll(linux_sll.clone()).header_len(),
+                LinuxSllHeader::LEN
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write(
+            ethernet2 in ethernet_2_any(),
+            linux_sll in linux_sll_any()
+        ) {
+            // ethernet2
+            {
+                //write
+                {
+                    let result_input = {
+                        let mut buffer = Vec::new();
+                        ethernet2.write(&mut buffer).unwrap();
+                        buffer
+                    };
+                    let result_transport = {
+                        let mut buffer = Vec::new();
+                        LinkHeader::Ethernet2(ethernet2.clone()).write(&mut buffer).unwrap();
+                        buffer
+                    };
+                    assert_eq!(result_input, result_transport);
+                }
+                //trigger an error
+                {
+                    let mut a: [u8;0] = [];
+                    assert!(
+                        LinkHeader::Ethernet2(ethernet2.clone())
+                        .write(&mut Cursor::new(&mut a[..]))
+                        .is_err()
+                    );
+                }
+            }
+            // linux_sll
+            {
+                //write
+                {
+                    let result_input = {
+                        let mut buffer = Vec::new();
+                        linux_sll.write(&mut buffer).unwrap();
+                        buffer
+                    };
+                    let result_transport = {
+                        let mut buffer = Vec::new();
+                        LinkHeader::LinuxSll(linux_sll.clone()).write(&mut buffer).unwrap();
+                        buffer
+                    };
+                    assert_eq!(result_input, result_transport);
+                }
+                //trigger an error
+                {
+                    let mut a: [u8;0] = [];
+                    assert!(
+                        LinkHeader::LinuxSll(linux_sll.clone())
+                        .write(&mut Cursor::new(&mut a[..]))
+                        .is_err()
+                    );
+                }
+            }
+        }
+    }
+}
diff --git a/src/link/link_slice.rs b/src/link/link_slice.rs
new file mode 100644
index 0000000..3257e6c
--- /dev/null
+++ b/src/link/link_slice.rs
@@ -0,0 +1,190 @@
+use crate::*;
+
+/// A slice containing the link layer header (currently only Ethernet II and
+/// SLL are supported).
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum LinkSlice<'a> {
+    /// A slice containing an Ethernet II header.
+    Ethernet2(Ethernet2Slice<'a>),
+
+    /// A slice containing a Linux Cooked Capture v1 (SLL) header.
+    LinuxSll(LinuxSllSlice<'a>),
+
+    /// Ether payload without header.
+    EtherPayload(EtherPayloadSlice<'a>),
+
+    /// Sll payload without header.
+    LinuxSllPayload(LinuxSllPayloadSlice<'a>),
+}
+
+impl<'a> LinkSlice<'a> {
+    /// Convert the link slice to a header
+    pub fn to_header(&self) -> Option<LinkHeader> {
+        use LinkSlice::*;
+        match self {
+            Ethernet2(slice) => Some(LinkHeader::Ethernet2(slice.to_header())),
+            LinuxSll(slice) => Some(LinkHeader::LinuxSll(slice.to_header())),
+            EtherPayload(_) => None,
+            LinuxSllPayload(_) => None,
+        }
+    }
+
+    /// Returns the link layer ether payload (slice + ether type number).
+    pub fn ether_payload(&self) -> Option<EtherPayloadSlice<'a>> {
+        use LinkSlice::*;
+        match self {
+            Ethernet2(s) => Some(s.payload().clone()),
+            LinuxSll(s) => Some(EtherPayloadSlice::try_from(s.payload()).ok()?.clone()),
+            EtherPayload(p) => Some(p.clone()),
+            LinuxSllPayload(p) => Some(EtherPayloadSlice::try_from(p.clone()).ok()?),
+        }
+    }
+
+    /// Returns the link layer sll payload (slice + link layer protocol type).
+    pub fn sll_payload(&self) -> LinuxSllPayloadSlice<'a> {
+        use LinkSlice::*;
+        match self {
+            Ethernet2(s) => LinuxSllPayloadSlice::from(s.payload().clone()),
+            LinuxSll(s) => s.payload().clone(),
+            EtherPayload(p) => LinuxSllPayloadSlice::from(p.clone()),
+            LinuxSllPayload(p) => p.clone(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn debug_clone_eq(ref eth in ethernet_2_unknown()) {
+            let bytes = eth.to_bytes();
+            let e = Ethernet2Slice::from_slice_without_fcs(&bytes).unwrap();
+            let slice = LinkSlice::Ethernet2(
+                e.clone()
+            );
+
+            // clone & eq
+            assert_eq!(slice.clone(), slice);
+
+            // debug
+            assert_eq!(
+                format!("{:?}", slice),
+                format!("Ethernet2({:?})", e),
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_header(
+            ref eth in ethernet_2_unknown(),
+            ref linux_sll in linux_sll_any()
+        ) {
+            {
+                let bytes = eth.to_bytes();
+                let slice = LinkSlice::Ethernet2(
+                    Ethernet2Slice::from_slice_without_fcs(&bytes).unwrap()
+                );
+                assert_eq!(
+                    slice.to_header(),
+                    Some(LinkHeader::Ethernet2(eth.clone()))
+                );
+            }
+            {
+                let bytes = linux_sll.to_bytes();
+                let slice = LinkSlice::LinuxSll(
+                    LinuxSllSlice::from_slice(&bytes).unwrap()
+                );
+                assert_eq!(
+                    slice.to_header(),
+                    Some(LinkHeader::LinuxSll(linux_sll.clone()))
+                );
+            }
+            {
+                let slice = LinkSlice::EtherPayload(EtherPayloadSlice {
+                    ether_type: ether_type::IPV4,
+                    payload: &[]
+                });
+                assert_eq!(
+                    slice.to_header(),
+                    None
+                );
+            }
+            {
+                let slice = LinkSlice::LinuxSllPayload(LinuxSllPayloadSlice {
+                    protocol_type: LinuxSllProtocolType::EtherType(ether_type::IPV4),
+                    payload: &[]
+                });
+                assert_eq!(
+                    slice.to_header(),
+                    None
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn ether_payload(
+            ref eth in ethernet_2_unknown(),
+            ref linux_sll in linux_sll_any()
+        ) {
+            let p = [1,2,3,4];
+            {
+                let mut bytes = Vec::with_capacity(Ethernet2Header::LEN + p.len());
+                bytes.extend_from_slice(&eth.to_bytes());
+                bytes.extend_from_slice(&p);
+                let slice = LinkSlice::Ethernet2(
+                    Ethernet2Slice::from_slice_without_fcs(&bytes).unwrap()
+                );
+                assert_eq!(
+                    slice.ether_payload().unwrap(),
+                    EtherPayloadSlice{ ether_type: eth.ether_type, payload: &p }
+                );
+            }
+            {
+                let slice = LinkSlice::EtherPayload(EtherPayloadSlice {
+                    ether_type: eth.ether_type,
+                    payload: &p
+                });
+                assert_eq!(
+                    slice.ether_payload().unwrap(),
+                    EtherPayloadSlice{ ether_type: eth.ether_type, payload: &p }
+                );
+            }
+            {
+                let mut bytes = Vec::with_capacity(LinuxSllHeader::LEN + p.len());
+                bytes.extend_from_slice(&linux_sll.to_bytes());
+                bytes.extend_from_slice(&p);
+                let slice = LinkSlice::LinuxSll(
+                    LinuxSllSlice::from_slice(&bytes).unwrap()
+                );
+                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 }
+                    );}
+                    _ => { assert!(slice.ether_payload().is_none());}
+                }
+            }
+            {
+                let slice = LinkSlice::LinuxSllPayload(LinuxSllPayloadSlice {
+                    protocol_type: linux_sll.protocol_type,
+                    payload: &p
+                });
+                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 }
+                    );}
+                    _ => { assert!(slice.ether_payload().is_none());}
+                }
+            }
+        }
+    }
+}
diff --git a/src/link/linux_nonstandard_ether_type.rs b/src/link/linux_nonstandard_ether_type.rs
new file mode 100644
index 0000000..24f151c
--- /dev/null
+++ b/src/link/linux_nonstandard_ether_type.rs
@@ -0,0 +1,500 @@
+/// Represents an non standard ethertype. These are defined in the Linux
+/// kernel with ids under 1500 so they don't clash with the standard ones.
+///
+/// You can convert any valid `u16` value to an `LinuxNonstandardEtherType` and
+/// the other way around.
+///
+/// ```
+/// use etherparse::LinuxNonstandardEtherType;
+///
+/// // Convert to LinuxNonstandardEtherType using the from & into trait
+/// let link_type: LinuxNonstandardEtherType = 0x0001.try_into().unwrap();
+/// assert_eq!(LinuxNonstandardEtherType::N802_3, link_type);
+///
+/// // convert to u16 using the from & into trait
+/// let num: u16 = LinuxNonstandardEtherType::N802_3.try_into().unwrap();
+/// assert_eq!(0x0001, num);
+/// ```
+#[derive(Clone, Copy, Eq, PartialEq)]
+pub struct LinuxNonstandardEtherType(pub(crate) u16);
+
+impl LinuxNonstandardEtherType {
+    // Numbers sourced from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/if_ether.h?id=e33c4963bf536900f917fb65a687724d5539bc21
+
+    pub const N802_3: LinuxNonstandardEtherType = Self(0x0001);
+    pub const AX25: LinuxNonstandardEtherType = Self(0x0002);
+    pub const ALL: LinuxNonstandardEtherType = Self(0x0003);
+    pub const N802_2: LinuxNonstandardEtherType = Self(0x0004);
+    pub const SNAP: LinuxNonstandardEtherType = Self(0x0005);
+    pub const DDCMP: LinuxNonstandardEtherType = Self(0x0006);
+    pub const WAN_PPP: LinuxNonstandardEtherType = Self(0x0007);
+    pub const PPP_MP: LinuxNonstandardEtherType = Self(0x0008);
+    pub const LOCALTALK: LinuxNonstandardEtherType = Self(0x0009);
+    pub const CAN: LinuxNonstandardEtherType = Self(0x000C);
+    pub const CANFD: LinuxNonstandardEtherType = Self(0x000D);
+    pub const CANXL: LinuxNonstandardEtherType = Self(0x000E);
+    pub const PPPTALK: LinuxNonstandardEtherType = Self(0x0010);
+    pub const TR_802_2: LinuxNonstandardEtherType = Self(0x0011);
+    pub const MOBITEX: LinuxNonstandardEtherType = Self(0x0015);
+    pub const CONTROL: LinuxNonstandardEtherType = Self(0x0016);
+    pub const IRDA: LinuxNonstandardEtherType = Self(0x0017);
+    pub const ECONET: LinuxNonstandardEtherType = Self(0x0018);
+    pub const HDLC: LinuxNonstandardEtherType = Self(0x0019);
+    pub const ARCNET: LinuxNonstandardEtherType = Self(0x001A);
+    pub const DSA: LinuxNonstandardEtherType = Self(0x001B);
+    pub const TRAILER: LinuxNonstandardEtherType = Self(0x001C);
+    pub const PHONET: LinuxNonstandardEtherType = Self(0x00F5);
+    pub const IEEE802154: LinuxNonstandardEtherType = Self(0x00F6);
+    pub const CAIF: LinuxNonstandardEtherType = Self(0x00F7);
+    pub const XDSA: LinuxNonstandardEtherType = Self(0x00F8);
+    pub const MAP: LinuxNonstandardEtherType = Self(0x00F9);
+    pub const MCTP: LinuxNonstandardEtherType = Self(0x00FA);
+}
+
+impl Default for LinuxNonstandardEtherType {
+    fn default() -> Self {
+        Self::N802_3
+    }
+}
+
+impl TryFrom<u16> for LinuxNonstandardEtherType {
+    type Error = ();
+
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        match value {
+            0x0000 => Err(()),
+            0x0001 => Ok(LinuxNonstandardEtherType::N802_3),
+            0x0002 => Ok(LinuxNonstandardEtherType::AX25),
+            0x0003 => Ok(LinuxNonstandardEtherType::ALL),
+            0x0004 => Ok(LinuxNonstandardEtherType::N802_2),
+            0x0005 => Ok(LinuxNonstandardEtherType::SNAP),
+            0x0006 => Ok(LinuxNonstandardEtherType::DDCMP),
+            0x0007 => Ok(LinuxNonstandardEtherType::WAN_PPP),
+            0x0008 => Ok(LinuxNonstandardEtherType::PPP_MP),
+            0x0009 => Ok(LinuxNonstandardEtherType::LOCALTALK),
+            0x000A..=0x000B => Err(()),
+            0x000C => Ok(LinuxNonstandardEtherType::CAN),
+            0x000D => Ok(LinuxNonstandardEtherType::CANFD),
+            0x000E => Ok(LinuxNonstandardEtherType::CANXL),
+            0x000F => Err(()),
+            0x0010 => Ok(LinuxNonstandardEtherType::PPPTALK),
+            0x0011 => Ok(LinuxNonstandardEtherType::TR_802_2),
+            0x0012..=0x0014 => Err(()),
+            0x0015 => Ok(LinuxNonstandardEtherType::MOBITEX),
+            0x0016 => Ok(LinuxNonstandardEtherType::CONTROL),
+            0x0017 => Ok(LinuxNonstandardEtherType::IRDA),
+            0x0018 => Ok(LinuxNonstandardEtherType::ECONET),
+            0x0019 => Ok(LinuxNonstandardEtherType::HDLC),
+            0x001A => Ok(LinuxNonstandardEtherType::ARCNET),
+            0x001B => Ok(LinuxNonstandardEtherType::DSA),
+            0x001C => Ok(LinuxNonstandardEtherType::TRAILER),
+            0x001D..=0x00F4 => Err(()),
+            0x00F5 => Ok(LinuxNonstandardEtherType::PHONET),
+            0x00F6 => Ok(LinuxNonstandardEtherType::IEEE802154),
+            0x00F7 => Ok(LinuxNonstandardEtherType::CAIF),
+            0x00F8 => Ok(LinuxNonstandardEtherType::XDSA),
+            0x00F9 => Ok(LinuxNonstandardEtherType::MAP),
+            0x00FA => Ok(LinuxNonstandardEtherType::MCTP),
+            0x00FB..=u16::MAX => Err(()),
+        }
+    }
+}
+
+impl From<LinuxNonstandardEtherType> for u16 {
+    #[inline]
+    fn from(val: LinuxNonstandardEtherType) -> Self {
+        val.0
+    }
+}
+
+impl core::fmt::Debug for LinuxNonstandardEtherType {
+    // Descriptions sourced from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/if_ether.h?id=e33c4963bf536900f917fb65a687724d5539bc21
+
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        match *self {
+            LinuxNonstandardEtherType(0x0000) => write!(f, "{:#06X} (Unknown)", self.0),
+            LinuxNonstandardEtherType::N802_3 => {
+                write!(f, "{:#06X} (Dummy type for 802.3 frames)", self.0)
+            }
+            LinuxNonstandardEtherType::AX25 => {
+                write!(f, "{:#06X} (Dummy protocol id for AX.25)", self.0)
+            }
+            LinuxNonstandardEtherType::ALL => write!(f, "{:#06X} (Every packet)", self.0),
+            LinuxNonstandardEtherType::N802_2 => write!(f, "{:#06X} (802.2 frames)", self.0),
+            LinuxNonstandardEtherType::SNAP => write!(f, "{:#06X} (SNAP: Internal only)", self.0),
+            LinuxNonstandardEtherType::DDCMP => {
+                write!(f, "{:#06X} (DEC DDCMP: Internal only)", self.0)
+            }
+            LinuxNonstandardEtherType::WAN_PPP => {
+                write!(f, "{:#06X} (Dummy type for WAN PPP frames)", self.0)
+            }
+            LinuxNonstandardEtherType::PPP_MP => {
+                write!(f, "{:#06X} (Dummy type for PPP MP frames)", self.0)
+            }
+            LinuxNonstandardEtherType::LOCALTALK => {
+                write!(f, "{:#06X} (Localtalk pseudo type)", self.0)
+            }
+            LinuxNonstandardEtherType(0x000A..=0x000B) => write!(f, "{:#06X} (Unknown)", self.0),
+            LinuxNonstandardEtherType::CAN => {
+                write!(f, "{:#06X} (CAN: Controller Area Network)", self.0)
+            }
+            LinuxNonstandardEtherType::CANFD => {
+                write!(f, "{:#06X} (CANFD: CAN flexible data rate)", self.0)
+            }
+            LinuxNonstandardEtherType::CANXL => {
+                write!(f, "{:#06X} (CANXL: eXtended frame Length)", self.0)
+            }
+            LinuxNonstandardEtherType(0x000F) => write!(f, "{:#06X} (Unknown)", self.0),
+            LinuxNonstandardEtherType::PPPTALK => {
+                write!(f, "{:#06X} (Dummy type for Atalk over PPP)", self.0)
+            }
+            LinuxNonstandardEtherType::TR_802_2 => write!(f, "{:#06X} (802.2 frames)", self.0),
+            LinuxNonstandardEtherType(0x0012..=0x0014) => write!(f, "{:#06X} (Unknown)", self.0),
+            LinuxNonstandardEtherType::MOBITEX => write!(f, "{:#06X} (Mobitex)", self.0),
+            LinuxNonstandardEtherType::CONTROL => {
+                write!(f, "{:#06X} (Card specific control frames)", self.0)
+            }
+            LinuxNonstandardEtherType::IRDA => write!(f, "{:#06X} (Linux-IrDA)", self.0),
+            LinuxNonstandardEtherType::ECONET => write!(f, "{:#06X} (Acorn Econet)", self.0),
+            LinuxNonstandardEtherType::HDLC => write!(f, "{:#06X} (HDLC frames)", self.0),
+            LinuxNonstandardEtherType::ARCNET => write!(f, "{:#06X} (1A for ArcNet)", self.0),
+            LinuxNonstandardEtherType::DSA => {
+                write!(f, "{:#06X} (Distributed Switch Arch)", self.0)
+            }
+            LinuxNonstandardEtherType::TRAILER => {
+                write!(f, "{:#06X} (Trailer switch tagging)", self.0)
+            }
+            LinuxNonstandardEtherType(0x001D..=0x00F4) => write!(f, "{:#06X} (Unknown)", self.0),
+            LinuxNonstandardEtherType::PHONET => write!(f, "{:#06X} (Nokia Phonet frame)", self.0),
+            LinuxNonstandardEtherType::IEEE802154 => {
+                write!(f, "{:#06X} (IEEE802.15.4 frame)", self.0)
+            }
+            LinuxNonstandardEtherType::CAIF => {
+                write!(f, "{:#06X} (ST-Ericsson CAIF protocol)", self.0)
+            }
+            LinuxNonstandardEtherType::XDSA => {
+                write!(f, "{:#06X} (Multiplexed DSA protocol)", self.0)
+            }
+            LinuxNonstandardEtherType::MAP => write!(
+                f,
+                "{:#06X} (Qualcomm multiplexing and aggregation protocol)",
+                self.0
+            ),
+            LinuxNonstandardEtherType::MCTP => write!(
+                f,
+                "{:#06X} (Management component transport protocol packets)",
+                self.0
+            ),
+            LinuxNonstandardEtherType(0x00FB..=u16::MAX) => write!(f, "{:#06X} (Unknown)", self.0),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use alloc::format;
+
+    #[test]
+    fn to_u16() {
+        assert_eq!(0x0001, u16::from(LinuxNonstandardEtherType::N802_3));
+        assert_eq!(0x0002, u16::from(LinuxNonstandardEtherType::AX25));
+        assert_eq!(0x0003, u16::from(LinuxNonstandardEtherType::ALL));
+        assert_eq!(0x0004, u16::from(LinuxNonstandardEtherType::N802_2));
+        assert_eq!(0x0005, u16::from(LinuxNonstandardEtherType::SNAP));
+        assert_eq!(0x0006, u16::from(LinuxNonstandardEtherType::DDCMP));
+        assert_eq!(0x0007, u16::from(LinuxNonstandardEtherType::WAN_PPP));
+        assert_eq!(0x0008, u16::from(LinuxNonstandardEtherType::PPP_MP));
+        assert_eq!(0x0009, u16::from(LinuxNonstandardEtherType::LOCALTALK));
+        assert_eq!(0x000C, u16::from(LinuxNonstandardEtherType::CAN));
+        assert_eq!(0x000D, u16::from(LinuxNonstandardEtherType::CANFD));
+        assert_eq!(0x000E, u16::from(LinuxNonstandardEtherType::CANXL));
+        assert_eq!(0x0010, u16::from(LinuxNonstandardEtherType::PPPTALK));
+        assert_eq!(0x0011, u16::from(LinuxNonstandardEtherType::TR_802_2));
+        assert_eq!(0x0015, u16::from(LinuxNonstandardEtherType::MOBITEX));
+        assert_eq!(0x0016, u16::from(LinuxNonstandardEtherType::CONTROL));
+        assert_eq!(0x0017, u16::from(LinuxNonstandardEtherType::IRDA));
+        assert_eq!(0x0018, u16::from(LinuxNonstandardEtherType::ECONET));
+        assert_eq!(0x0019, u16::from(LinuxNonstandardEtherType::HDLC));
+        assert_eq!(0x001A, u16::from(LinuxNonstandardEtherType::ARCNET));
+        assert_eq!(0x001B, u16::from(LinuxNonstandardEtherType::DSA));
+        assert_eq!(0x001C, u16::from(LinuxNonstandardEtherType::TRAILER));
+        assert_eq!(0x00F5, u16::from(LinuxNonstandardEtherType::PHONET));
+        assert_eq!(0x00F6, u16::from(LinuxNonstandardEtherType::IEEE802154));
+        assert_eq!(0x00F7, u16::from(LinuxNonstandardEtherType::CAIF));
+        assert_eq!(0x00F8, u16::from(LinuxNonstandardEtherType::XDSA));
+        assert_eq!(0x00F9, u16::from(LinuxNonstandardEtherType::MAP));
+        assert_eq!(0x00FA, u16::from(LinuxNonstandardEtherType::MCTP));
+    }
+
+    #[test]
+    fn try_from_u16() {
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x0001),
+            Ok(LinuxNonstandardEtherType::N802_3)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x0002),
+            Ok(LinuxNonstandardEtherType::AX25)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x0003),
+            Ok(LinuxNonstandardEtherType::ALL)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x0004),
+            Ok(LinuxNonstandardEtherType::N802_2)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x0005),
+            Ok(LinuxNonstandardEtherType::SNAP)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x0006),
+            Ok(LinuxNonstandardEtherType::DDCMP)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x0007),
+            Ok(LinuxNonstandardEtherType::WAN_PPP)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x0008),
+            Ok(LinuxNonstandardEtherType::PPP_MP)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x0009),
+            Ok(LinuxNonstandardEtherType::LOCALTALK)
+        );
+        /* 0x00A..=0x00B */
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x000C),
+            Ok(LinuxNonstandardEtherType::CAN)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x000D),
+            Ok(LinuxNonstandardEtherType::CANFD)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x000E),
+            Ok(LinuxNonstandardEtherType::CANXL)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x0010),
+            Ok(LinuxNonstandardEtherType::PPPTALK)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x0011),
+            Ok(LinuxNonstandardEtherType::TR_802_2)
+        );
+        /* 0x0012..=0x0014 */
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x0015),
+            Ok(LinuxNonstandardEtherType::MOBITEX)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x0016),
+            Ok(LinuxNonstandardEtherType::CONTROL)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x0017),
+            Ok(LinuxNonstandardEtherType::IRDA)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x0018),
+            Ok(LinuxNonstandardEtherType::ECONET)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x0019),
+            Ok(LinuxNonstandardEtherType::HDLC)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x001A),
+            Ok(LinuxNonstandardEtherType::ARCNET)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x001B),
+            Ok(LinuxNonstandardEtherType::DSA)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x001C),
+            Ok(LinuxNonstandardEtherType::TRAILER)
+        );
+        /* 0x001D..=0x00F4 */
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x00F5),
+            Ok(LinuxNonstandardEtherType::PHONET)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x00F6),
+            Ok(LinuxNonstandardEtherType::IEEE802154)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x00F7),
+            Ok(LinuxNonstandardEtherType::CAIF)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x00F8),
+            Ok(LinuxNonstandardEtherType::XDSA)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x00F9),
+            Ok(LinuxNonstandardEtherType::MAP)
+        );
+        assert_eq!(
+            LinuxNonstandardEtherType::try_from(0x00FA),
+            Ok(LinuxNonstandardEtherType::MCTP)
+        );
+        /* 0x00FB..=u16::MAX */
+    }
+
+    #[test]
+    fn dbg() {
+        let pairs = &[
+            (
+                LinuxNonstandardEtherType::N802_3,
+                "0x0001 (Dummy type for 802.3 frames)",
+            ),
+            (
+                LinuxNonstandardEtherType::AX25,
+                "0x0002 (Dummy protocol id for AX.25)",
+            ),
+            (LinuxNonstandardEtherType::ALL, "0x0003 (Every packet)"),
+            (LinuxNonstandardEtherType::N802_2, "0x0004 (802.2 frames)"),
+            (
+                LinuxNonstandardEtherType::SNAP,
+                "0x0005 (SNAP: Internal only)",
+            ),
+            (
+                LinuxNonstandardEtherType::DDCMP,
+                "0x0006 (DEC DDCMP: Internal only)",
+            ),
+            (
+                LinuxNonstandardEtherType::WAN_PPP,
+                "0x0007 (Dummy type for WAN PPP frames)",
+            ),
+            (
+                LinuxNonstandardEtherType::PPP_MP,
+                "0x0008 (Dummy type for PPP MP frames)",
+            ),
+            (
+                LinuxNonstandardEtherType::LOCALTALK,
+                "0x0009 (Localtalk pseudo type)",
+            ),
+            (
+                LinuxNonstandardEtherType::CAN,
+                "0x000C (CAN: Controller Area Network)",
+            ),
+            (
+                LinuxNonstandardEtherType::CANFD,
+                "0x000D (CANFD: CAN flexible data rate)",
+            ),
+            (
+                LinuxNonstandardEtherType::CANXL,
+                "0x000E (CANXL: eXtended frame Length)",
+            ),
+            (
+                LinuxNonstandardEtherType::PPPTALK,
+                "0x0010 (Dummy type for Atalk over PPP)",
+            ),
+            (LinuxNonstandardEtherType::TR_802_2, "0x0011 (802.2 frames)"),
+            (LinuxNonstandardEtherType::MOBITEX, "0x0015 (Mobitex)"),
+            (
+                LinuxNonstandardEtherType::CONTROL,
+                "0x0016 (Card specific control frames)",
+            ),
+            (LinuxNonstandardEtherType::IRDA, "0x0017 (Linux-IrDA)"),
+            (LinuxNonstandardEtherType::ECONET, "0x0018 (Acorn Econet)"),
+            (LinuxNonstandardEtherType::HDLC, "0x0019 (HDLC frames)"),
+            (LinuxNonstandardEtherType::ARCNET, "0x001A (1A for ArcNet)"),
+            (
+                LinuxNonstandardEtherType::DSA,
+                "0x001B (Distributed Switch Arch)",
+            ),
+            (
+                LinuxNonstandardEtherType::TRAILER,
+                "0x001C (Trailer switch tagging)",
+            ),
+            (
+                LinuxNonstandardEtherType::PHONET,
+                "0x00F5 (Nokia Phonet frame)",
+            ),
+            (
+                LinuxNonstandardEtherType::IEEE802154,
+                "0x00F6 (IEEE802.15.4 frame)",
+            ),
+            (
+                LinuxNonstandardEtherType::CAIF,
+                "0x00F7 (ST-Ericsson CAIF protocol)",
+            ),
+            (
+                LinuxNonstandardEtherType::XDSA,
+                "0x00F8 (Multiplexed DSA protocol)",
+            ),
+            (
+                LinuxNonstandardEtherType::MAP,
+                "0x00F9 (Qualcomm multiplexing and aggregation protocol)",
+            ),
+            (
+                LinuxNonstandardEtherType::MCTP,
+                "0x00FA (Management component transport protocol packets)",
+            ),
+        ];
+
+        for (ether_type, str_value) in pairs {
+            assert_eq!(str_value, &format!("{:?}", ether_type));
+        }
+    }
+
+    #[test]
+    fn default() {
+        let value: LinuxNonstandardEtherType = Default::default();
+        assert_eq!(LinuxNonstandardEtherType::N802_3, value);
+    }
+
+    #[test]
+    fn clone_eq() {
+        let values = &[
+            LinuxNonstandardEtherType::N802_3,
+            LinuxNonstandardEtherType::AX25,
+            LinuxNonstandardEtherType::ALL,
+            LinuxNonstandardEtherType::N802_2,
+            LinuxNonstandardEtherType::SNAP,
+            LinuxNonstandardEtherType::DDCMP,
+            LinuxNonstandardEtherType::WAN_PPP,
+            LinuxNonstandardEtherType::PPP_MP,
+            LinuxNonstandardEtherType::LOCALTALK,
+            LinuxNonstandardEtherType::CAN,
+            LinuxNonstandardEtherType::CANFD,
+            LinuxNonstandardEtherType::CANXL,
+            LinuxNonstandardEtherType::PPPTALK,
+            LinuxNonstandardEtherType::TR_802_2,
+            LinuxNonstandardEtherType::MOBITEX,
+            LinuxNonstandardEtherType::CONTROL,
+            LinuxNonstandardEtherType::IRDA,
+            LinuxNonstandardEtherType::ECONET,
+            LinuxNonstandardEtherType::HDLC,
+            LinuxNonstandardEtherType::ARCNET,
+            LinuxNonstandardEtherType::DSA,
+            LinuxNonstandardEtherType::TRAILER,
+            LinuxNonstandardEtherType::PHONET,
+            LinuxNonstandardEtherType::IEEE802154,
+            LinuxNonstandardEtherType::CAIF,
+            LinuxNonstandardEtherType::XDSA,
+            LinuxNonstandardEtherType::MAP,
+            LinuxNonstandardEtherType::MCTP,
+        ];
+
+        // clone
+        for v in values {
+            assert_eq!(v, &v.clone());
+        }
+
+        // eq
+        for (a_pos, a) in values.iter().enumerate() {
+            for (b_pos, b) in values.iter().enumerate() {
+                assert_eq!(a_pos == b_pos, a == b);
+                assert_eq!(a_pos != b_pos, a != b);
+            }
+        }
+    }
+}
diff --git a/src/link/linux_sll_header.rs b/src/link/linux_sll_header.rs
new file mode 100644
index 0000000..ec1a1ba
--- /dev/null
+++ b/src/link/linux_sll_header.rs
@@ -0,0 +1,333 @@
+use crate::{err, ArpHardwareId, LinuxSllHeaderSlice, LinuxSllPacketType, LinuxSllProtocolType};
+
+/// Linux Cooked Capture v1 (SLL) Header
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct LinuxSllHeader {
+    /// Type of the captured packet
+    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
+    pub sender_address_valid_length: u16,
+    /// The link-layer adress 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.
+    pub sender_address: [u8; 8],
+    /// The protocol type of the encapsulated packet
+    pub protocol_type: LinuxSllProtocolType,
+}
+
+impl LinuxSllHeader {
+    /// Serialized size of an SLL header in bytes/octets.
+    pub const LEN: usize = 16;
+
+    /// Read an SLL header from a slice and return the header & unused parts of the slice.
+    #[inline]
+    pub fn from_slice(
+        slice: &[u8],
+    ) -> Result<(LinuxSllHeader, &[u8]), err::linux_sll::HeaderSliceError> {
+        Ok((
+            LinuxSllHeaderSlice::from_slice(slice)?.to_header(),
+            &slice[LinuxSllHeader::LEN..],
+        ))
+    }
+
+    /// Read an SLL header from a static sized byte array.
+    #[inline]
+    pub fn from_bytes(bytes: [u8; 16]) -> Result<LinuxSllHeader, err::linux_sll::HeaderError> {
+        let packet_type = LinuxSllPacketType::try_from(u16::from_be_bytes([bytes[0], bytes[1]]))?;
+        let arp_hrd_type = ArpHardwareId::from(u16::from_be_bytes([bytes[2], bytes[3]]));
+        let sender_address_valid_length = u16::from_be_bytes([bytes[4], bytes[5]]);
+        let sender_address = [
+            bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13],
+        ];
+        let protocol_type = LinuxSllProtocolType::try_from((
+            arp_hrd_type,
+            u16::from_be_bytes([bytes[14], bytes[15]]),
+        ))?;
+
+        Ok(LinuxSllHeader {
+            packet_type,
+            arp_hrd_type,
+            sender_address_valid_length,
+            sender_address,
+            protocol_type,
+        })
+    }
+
+    /// Reads an SLL header from the current position of the read argument.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut T,
+    ) -> Result<LinuxSllHeader, err::ReadError> {
+        let buffer = {
+            let mut buffer = [0; LinuxSllHeader::LEN];
+            reader.read_exact(&mut buffer)?;
+            buffer
+        };
+
+        Ok(
+            // SAFETY: Safe as the buffer contains exactly the needed LinuxSllHeader::LEN bytes.
+            unsafe { LinuxSllHeaderSlice::from_slice_unchecked(&buffer) }.to_header(),
+        )
+    }
+
+    /// Serialize the header to a given slice. Returns the unused part of the slice.
+    pub fn write_to_slice<'a>(
+        &self,
+        slice: &'a mut [u8],
+    ) -> Result<&'a mut [u8], err::SliceWriteSpaceError> {
+        // length check
+        if slice.len() < LinuxSllHeader::LEN {
+            Err(err::SliceWriteSpaceError {
+                required_len: LinuxSllHeader::LEN,
+                len: slice.len(),
+                layer: err::Layer::LinuxSllHeader,
+                layer_start_offset: 0,
+            })
+        } else {
+            slice[..LinuxSllHeader::LEN].copy_from_slice(&self.to_bytes());
+            Ok(&mut slice[LinuxSllHeader::LEN..])
+        }
+    }
+
+    /// Writes a given Sll header to the current position of the write argument.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    #[inline]
+    pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
+        writer.write_all(&self.to_bytes())
+    }
+
+    /// Length of the serialized header in bytes.
+    #[inline]
+    pub fn header_len(&self) -> usize {
+        Self::LEN
+    }
+
+    /// Returns the serialized form of the header as a statically
+    /// sized byte array.
+    #[inline]
+    pub fn to_bytes(&self) -> [u8; Self::LEN] {
+        let packet_type_be = u16::from(self.packet_type).to_be_bytes();
+        let arp_hrd_type_be = u16::from(self.arp_hrd_type).to_be_bytes();
+        let sender_address_valid_length_be = self.sender_address_valid_length.to_be_bytes();
+        let sender_address_be = self.sender_address;
+        let protocol_type_be = u16::from(self.protocol_type).to_be_bytes();
+
+        [
+            packet_type_be[0],
+            packet_type_be[1],
+            arp_hrd_type_be[0],
+            arp_hrd_type_be[1],
+            sender_address_valid_length_be[0],
+            sender_address_valid_length_be[1],
+            sender_address_be[0],
+            sender_address_be[1],
+            sender_address_be[2],
+            sender_address_be[3],
+            sender_address_be[4],
+            sender_address_be[5],
+            sender_address_be[6],
+            sender_address_be[7],
+            protocol_type_be[0],
+            protocol_type_be[1],
+        ]
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::{test_gens::*, LenSource};
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+    use std::io::{Cursor, ErrorKind};
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            input in linux_sll_any(),
+            dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
+        ) {
+            // serialize
+            let mut buffer: Vec<u8> = Vec::with_capacity(LinuxSllHeader::LEN + dummy_data.len());
+            input.write(&mut buffer).unwrap();
+            buffer.extend(&dummy_data[..]);
+
+            // calls with a valid result
+            {
+                let (result, rest) = LinuxSllHeader::from_slice(&buffer[..]).unwrap();
+                assert_eq!(input, result);
+                assert_eq!(&buffer[16..], rest);
+            }
+
+            // call with not enough data in the slice
+            for len in 0..=13 {
+                assert_eq!(
+                    LinuxSllHeader::from_slice(&buffer[..len]).unwrap_err(),
+                    err::linux_sll::HeaderSliceError::Len(err::LenError{
+                        required_len: LinuxSllHeader::LEN,
+                        len: len,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::LinuxSllHeader,
+                        layer_start_offset: 0,
+                    })
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_bytes(input in linux_sll_any()) {
+            assert_eq!(
+                input,
+                LinuxSllHeader::from_bytes(input.to_bytes()).unwrap()
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read(
+            input in linux_sll_any(),
+            dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
+        ) {
+            // normal read
+            let mut buffer = Vec::with_capacity(LinuxSllHeader::LEN + dummy_data.len());
+            input.write(&mut buffer).unwrap();
+            buffer.extend(&dummy_data[..]);
+
+            // calls with a valid result
+            {
+                let mut cursor = Cursor::new(&buffer);
+                let result = LinuxSllHeader::read(&mut cursor).unwrap();
+                assert_eq!(input, result);
+                assert_eq!(cursor.position(), u64::try_from(LinuxSllHeader::LEN).unwrap());
+            }
+
+            // unexpected eof
+            for len in 0..=13 {
+                let mut cursor = Cursor::new(&buffer[0..len]);
+                assert_eq!(
+                    LinuxSllHeader::read(&mut cursor)
+                    .unwrap_err()
+                    .io().unwrap()
+                    .kind(),
+                    ErrorKind::UnexpectedEof
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write_to_slice(input in linux_sll_any()) {
+            // normal write
+            {
+                let mut buffer: [u8;LinuxSllHeader::LEN] = [0;LinuxSllHeader::LEN];
+                input.write_to_slice(&mut buffer).unwrap();
+                assert_eq!(buffer, input.to_bytes());
+            }
+            // len to small
+            for len in 0..14 {
+                let mut buffer: [u8;LinuxSllHeader::LEN] = [0;LinuxSllHeader::LEN];
+                assert_eq!(
+                    err::SliceWriteSpaceError {
+                        required_len: LinuxSllHeader::LEN,
+                        len,
+                        layer: err::Layer::LinuxSllHeader,
+                        layer_start_offset: 0,
+                    },
+                    input.write_to_slice(&mut buffer[..len]).unwrap_err()
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write(input in linux_sll_any()) {
+            // successful write
+            {
+                let mut buffer: Vec<u8> = Vec::with_capacity(LinuxSllHeader::LEN);
+                input.write(&mut buffer).unwrap();
+                assert_eq!(&buffer[..], &input.to_bytes());
+            }
+
+            // not enough memory for write (unexpected eof)
+            for len in 0..8 {
+                let mut buffer = [0u8;8];
+                let mut writer = Cursor::new(&mut buffer[..len]);
+                assert!(input.write(&mut writer).is_err());
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header_len(input in linux_sll_any()) {
+            assert_eq!(input.header_len(), LinuxSllHeader::LEN);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_bytes(input in linux_sll_any()) {
+            let packet_type_be = u16::from(input.packet_type).to_be_bytes();
+            let arp_hrd_type_be = u16::from(input.arp_hrd_type).to_be_bytes();
+            let sender_address_valid_length_be = input.sender_address_valid_length.to_be_bytes();
+            let sender_address_be = input.sender_address;
+            let protocol_type_be = u16::from(input.protocol_type).to_be_bytes();
+
+            assert_eq!(
+                input.to_bytes(),
+                [
+                    packet_type_be[0],
+                    packet_type_be[1],
+                    arp_hrd_type_be[0],
+                    arp_hrd_type_be[1],
+                    sender_address_valid_length_be[0],
+                    sender_address_valid_length_be[1],
+                    sender_address_be[0],
+                    sender_address_be[1],
+                    sender_address_be[2],
+                    sender_address_be[3],
+                    sender_address_be[4],
+                    sender_address_be[5],
+                    sender_address_be[6],
+                    sender_address_be[7],
+                    protocol_type_be[0],
+                    protocol_type_be[1],
+                ]
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(input in linux_sll_any()) {
+            assert_eq!(input, input.clone());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn dbg(input in linux_sll_any()) {
+            assert_eq!(
+                &format!(
+                    "LinuxSllHeader {{ packet_type: {:?}, arp_hrd_type: {:?}, sender_address_valid_length: {:?}, sender_address: {:?}, protocol_type: {:?} }}",
+                    input.packet_type,
+                    input.arp_hrd_type,
+                    input.sender_address_valid_length,
+                    input.sender_address,
+                    input.protocol_type,
+                ),
+                &format!("{:?}", input)
+            );
+        }
+    }
+}
diff --git a/src/link/linux_sll_header_slice.rs b/src/link/linux_sll_header_slice.rs
new file mode 100644
index 0000000..274cbab
--- /dev/null
+++ b/src/link/linux_sll_header_slice.rs
@@ -0,0 +1,249 @@
+use crate::*;
+use core::{cmp::min, slice::from_raw_parts};
+
+///A slice containing an Linux Cooked Capture (SLL) header of a network package.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct LinuxSllHeaderSlice<'a> {
+    slice: &'a [u8],
+}
+
+impl<'a> LinuxSllHeaderSlice<'a> {
+    /// Creates a SLL header slice from an other slice.
+    pub fn from_slice(
+        slice: &'a [u8],
+    ) -> Result<LinuxSllHeaderSlice<'a>, err::linux_sll::HeaderSliceError> {
+        //check length
+        if slice.len() < LinuxSllHeader::LEN {
+            return Err(err::linux_sll::HeaderSliceError::Len(err::LenError {
+                required_len: LinuxSllHeader::LEN,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::LinuxSllHeader,
+                layer_start_offset: 0,
+            }));
+        }
+
+        // check valid packet type
+
+        // SAFETY:
+        // Safe as it is checked at the start of the function that the
+        // length of the slice is at least LinuxSllHeader::LEN (16).
+        let packet_type_val = unsafe { get_unchecked_be_u16(slice.as_ptr()) };
+        if let Err(err) = LinuxSllPacketType::try_from(packet_type_val) {
+            return Err(err::linux_sll::HeaderSliceError::Content(err));
+        }
+
+        // check supported ArpHardwareId
+
+        // SAFETY:
+        // Safe as it is checked at the start of the function that the
+        // length of the slice is at least LinuxSllHeader::LEN (16).
+        let arp_hardware_id = unsafe { get_unchecked_be_u16(slice.as_ptr().add(2)) };
+        let arp_hardware_id = ArpHardwareId::from(arp_hardware_id);
+
+        // SAFETY:
+        // Safe as it is checked at the start of the function that the
+        // length of the slice is at least LinuxSllHeader::LEN (16).
+        let protocol_type = unsafe { get_unchecked_be_u16(slice.as_ptr().add(14)) };
+
+        if let Err(err) = LinuxSllProtocolType::try_from((arp_hardware_id, protocol_type)) {
+            return Err(err::linux_sll::HeaderSliceError::Content(err));
+        }
+
+        //all done
+        Ok(LinuxSllHeaderSlice {
+            // SAFETY:
+            // Safe as slice length is checked to be at least
+            // LinuxSllHeader::LEN (16) before this.
+            slice: unsafe { from_raw_parts(slice.as_ptr(), LinuxSllHeader::LEN) },
+        })
+    }
+
+    /// Converts the given slice into a SLL header slice WITHOUT any checks to
+    /// ensure that the data present is an sll header or that the slice length
+    /// is matching the header length.
+    ///
+    /// If you are not sure what this means, use [`LinuxSllHeaderSlice::from_slice`]
+    /// instead.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensured that the given slice has the length of
+    /// [`LinuxSllHeader::LEN`] and the fields are valid
+    #[inline]
+    #[cfg(feature = "std")]
+    pub(crate) unsafe fn from_slice_unchecked(slice: &[u8]) -> LinuxSllHeaderSlice {
+        debug_assert!(slice.len() == LinuxSllHeader::LEN);
+        LinuxSllHeaderSlice { slice }
+    }
+
+    /// Returns the slice containing the SLL header
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+
+    /// Read the packet type field.
+    #[inline]
+    pub fn packet_type(&self) -> LinuxSllPacketType {
+        // SAFETY:
+        // Safe as the contructor 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()) };
+
+        // SAFETY:
+        // Safe as the constructor checks that the packet type is valid
+        unsafe { LinuxSllPacketType::try_from(packet_type_raw).unwrap_unchecked() }
+    }
+
+    /// Read the arp hardware type field
+    #[inline]
+    pub fn arp_hardware_type(&self) -> ArpHardwareId {
+        // SAFETY:
+        // Safe as the contructor 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)) };
+
+        ArpHardwareId::from(arp_hardware_type_raw)
+    }
+
+    /// Read the link layer address length field.
+    #[inline]
+    pub fn sender_address_valid_length(&self) -> u16 {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of LinuxSllHeader::LEN (16).
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(4)) }
+    }
+
+    /// Read the link layer address field. Only the first
+    /// `LinuxSllHeaderSlice::link_layer_address_length` bytes are meaningful
+    #[inline]
+    pub fn sender_address_full(&self) -> [u8; 8] {
+        // SAFETY:
+        // Safe as the contructor 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)) }
+    }
+
+    /// Get the meaningful bytes of the slice of the link layer address
+    #[inline]
+    pub fn sender_address(&self) -> &'a [u8] {
+        let length = self.sender_address_valid_length() as usize;
+        &self.slice[6..min(6 + length, 6 + 8)]
+    }
+
+    /// Read the protocol type field
+    #[inline]
+    pub fn protocol_type(&self) -> LinuxSllProtocolType {
+        let arp_harware_type = self.arp_hardware_type();
+        // SAFETY:
+        // Safe as the contructor 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
+        unsafe {
+            LinuxSllProtocolType::try_from((arp_harware_type, protocol_type_raw)).unwrap_unchecked()
+        }
+    }
+
+    /// Decode all the fields and copy the results to a [`LinuxSllHeader`] struct
+    pub fn to_header(&self) -> LinuxSllHeader {
+        LinuxSllHeader {
+            packet_type: self.packet_type(),
+            arp_hrd_type: self.arp_hardware_type(),
+            sender_address_valid_length: self.sender_address_valid_length(),
+            sender_address: self.sender_address_full(),
+            protocol_type: self.protocol_type(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            input in linux_sll_any(),
+            dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
+        ) {
+            // serialize
+            let mut buffer: Vec<u8> = Vec::with_capacity(LinuxSllHeader::LEN + dummy_data.len());
+            input.write(&mut buffer).unwrap();
+            buffer.extend(&dummy_data[..]);
+
+            // calls with a valid result
+            {
+                let result = LinuxSllHeaderSlice::from_slice(&buffer[..]).unwrap();
+                assert_eq!(&buffer[..LinuxSllHeader::LEN], result.slice());
+            }
+
+            // call with not enough data in the slice
+            for len in 0..=13 {
+                assert_eq!(
+                    LinuxSllHeaderSlice::from_slice(&buffer[..len]),
+                    Err(err::linux_sll::HeaderSliceError::Len(err::LenError{
+                        required_len: LinuxSllHeader::LEN,
+                        len: len,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::LinuxSllHeader,
+                        layer_start_offset: 0,
+                    }))
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn getters(input in linux_sll_any()) {
+            let buffer = input.to_bytes();
+            let slice = LinuxSllHeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(input.packet_type, slice.packet_type());
+            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.protocol_type, slice.protocol_type());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_header(input in linux_sll_any()) {
+            let buffer = input.to_bytes();
+            let slice = LinuxSllHeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(input, slice.to_header());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(input in linux_sll_any()) {
+            let buffer = input.to_bytes();
+            let slice = LinuxSllHeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(slice, slice.clone());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn dbg(input in linux_sll_any()) {
+            let buffer = input.to_bytes();
+            let slice = LinuxSllHeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(
+                &format!(
+                    "LinuxSllHeaderSlice {{ slice: {:?} }}",
+                    slice.slice()
+                ),
+                &format!("{:?}", slice)
+            );
+        }
+    }
+}
diff --git a/src/link/linux_sll_packet_type.rs b/src/link/linux_sll_packet_type.rs
new file mode 100644
index 0000000..78c62a3
--- /dev/null
+++ b/src/link/linux_sll_packet_type.rs
@@ -0,0 +1,201 @@
+use core::hint::unreachable_unchecked;
+
+use crate::err::{self};
+
+/// Represents an "Packet type", indicating the direction where it was sent,
+/// used inside a SLL header
+///
+/// You can convert `u16` in the valid range to an `LinuxSllType` and the
+/// other way around
+///
+/// ```
+/// use etherparse::LinuxSllPacketType;
+///
+/// // Convert to LinuxSllPacketType using the try_from & try_into trait
+/// let link_type: LinuxSllPacketType = 1_u16.try_into().unwrap();
+/// assert_eq!(LinuxSllPacketType::BROADCAST, link_type);
+///
+/// // convert to u16 using the from & into trait
+/// let num: u16 = LinuxSllPacketType::BROADCAST.into();
+/// assert_eq!(1, num);
+/// ```
+#[derive(Clone, Copy, Eq, PartialEq, Default)]
+pub struct LinuxSllPacketType(u16);
+
+impl LinuxSllPacketType {
+    // Numbers sourced from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/if_packet.h?id=e33c4963bf536900f917fb65a687724d5539bc21
+
+    pub const HOST: LinuxSllPacketType = Self(0);
+    pub const BROADCAST: LinuxSllPacketType = Self(1);
+    pub const MULTICAST: LinuxSllPacketType = Self(2);
+    pub const OTHERHOST: LinuxSllPacketType = Self(3);
+    pub const OUTGOING: LinuxSllPacketType = Self(4);
+    pub const LOOPBACK: LinuxSllPacketType = Self(5);
+    pub const USER: LinuxSllPacketType = Self(6);
+    pub const KERNEL: LinuxSllPacketType = Self(7);
+
+    pub const MAX_VAL: u16 = 7;
+    const FIRST_INVALID: u16 = LinuxSllPacketType::MAX_VAL + 1;
+}
+
+impl TryFrom<u16> for LinuxSllPacketType {
+    type Error = err::linux_sll::HeaderError;
+
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        match value {
+            0 => Ok(LinuxSllPacketType::HOST),
+            1 => Ok(LinuxSllPacketType::BROADCAST),
+            2 => Ok(LinuxSllPacketType::MULTICAST),
+            3 => Ok(LinuxSllPacketType::OTHERHOST),
+            4 => Ok(LinuxSllPacketType::OUTGOING),
+            5 => Ok(LinuxSllPacketType::LOOPBACK),
+            6 => Ok(LinuxSllPacketType::USER),
+            7 => Ok(LinuxSllPacketType::KERNEL),
+            LinuxSllPacketType::FIRST_INVALID..=u16::MAX => {
+                Err(err::linux_sll::HeaderError::UnsupportedPacketTypeField { packet_type: value })
+            }
+        }
+    }
+}
+
+impl From<LinuxSllPacketType> for u16 {
+    #[inline]
+    fn from(val: LinuxSllPacketType) -> Self {
+        val.0
+    }
+}
+
+impl core::fmt::Debug for LinuxSllPacketType {
+    // Descriptions sourced from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/if_packet.h?id=e33c4963bf536900f917fb65a687724d5539bc21
+
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        match self.0 {
+            0 => write!(f, "0 (Sent to us)"),
+            1 => write!(f, "1 (Sent to all)"),
+            2 => write!(f, "2 (Sent to group)"),
+            3 => write!(f, "3 (Sent to someone else)"),
+            4 => write!(f, "4 (Sent by us)"),
+            5 => write!(f, "5 (MC/BRD frame looped back)"),
+            6 => write!(f, "6 (Sent to user space)"),
+            7 => write!(f, "7 (Sent to kernel space)"),
+            LinuxSllPacketType::FIRST_INVALID..=u16::MAX => {
+                // SAFETY:
+                // Safe because values over MAX_VAL/FIRST_INVALID are never constructed
+                unsafe { unreachable_unchecked() }
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use alloc::format;
+
+    #[test]
+    fn to_u16() {
+        assert_eq!(0, u16::from(LinuxSllPacketType::HOST));
+        assert_eq!(1, u16::from(LinuxSllPacketType::BROADCAST));
+        assert_eq!(2, u16::from(LinuxSllPacketType::MULTICAST));
+        assert_eq!(3, u16::from(LinuxSllPacketType::OTHERHOST));
+        assert_eq!(4, u16::from(LinuxSllPacketType::OUTGOING));
+        assert_eq!(5, u16::from(LinuxSllPacketType::LOOPBACK));
+        assert_eq!(6, u16::from(LinuxSllPacketType::USER));
+        assert_eq!(7, u16::from(LinuxSllPacketType::KERNEL));
+    }
+
+    #[test]
+    fn try_from_u16() {
+        assert_eq!(
+            LinuxSllPacketType::try_from(0),
+            Ok(LinuxSllPacketType::HOST)
+        );
+        assert_eq!(
+            LinuxSllPacketType::try_from(1),
+            Ok(LinuxSllPacketType::BROADCAST)
+        );
+        assert_eq!(
+            LinuxSllPacketType::try_from(2),
+            Ok(LinuxSllPacketType::MULTICAST)
+        );
+        assert_eq!(
+            LinuxSllPacketType::try_from(3),
+            Ok(LinuxSllPacketType::OTHERHOST)
+        );
+        assert_eq!(
+            LinuxSllPacketType::try_from(4),
+            Ok(LinuxSllPacketType::OUTGOING)
+        );
+        assert_eq!(
+            LinuxSllPacketType::try_from(5),
+            Ok(LinuxSllPacketType::LOOPBACK)
+        );
+        assert_eq!(
+            LinuxSllPacketType::try_from(6),
+            Ok(LinuxSllPacketType::USER)
+        );
+        assert_eq!(
+            LinuxSllPacketType::try_from(7),
+            Ok(LinuxSllPacketType::KERNEL)
+        );
+        assert_eq!(
+            LinuxSllPacketType::try_from(8),
+            Err(err::linux_sll::HeaderError::UnsupportedPacketTypeField { packet_type: 8 })
+        );
+        assert_eq!(
+            LinuxSllPacketType::try_from(123),
+            Err(err::linux_sll::HeaderError::UnsupportedPacketTypeField { packet_type: 123 })
+        );
+    }
+
+    #[test]
+    fn dbg() {
+        let pairs = &[
+            (LinuxSllPacketType::HOST, "0 (Sent to us)"),
+            (LinuxSllPacketType::BROADCAST, "1 (Sent to all)"),
+            (LinuxSllPacketType::MULTICAST, "2 (Sent to group)"),
+            (LinuxSllPacketType::OTHERHOST, "3 (Sent to someone else)"),
+            (LinuxSllPacketType::OUTGOING, "4 (Sent by us)"),
+            (LinuxSllPacketType::LOOPBACK, "5 (MC/BRD frame looped back)"),
+            (LinuxSllPacketType::USER, "6 (Sent to user space)"),
+            (LinuxSllPacketType::KERNEL, "7 (Sent to kernel space)"),
+        ];
+
+        for (ether_type, str_value) in pairs {
+            assert_eq!(str_value, &format!("{:?}", ether_type));
+        }
+    }
+
+    #[test]
+    fn default() {
+        let value: LinuxSllPacketType = Default::default();
+        assert_eq!(LinuxSllPacketType::HOST, value);
+    }
+
+    #[test]
+    fn clone_eq() {
+        let values = &[
+            LinuxSllPacketType::HOST,
+            LinuxSllPacketType::BROADCAST,
+            LinuxSllPacketType::MULTICAST,
+            LinuxSllPacketType::OTHERHOST,
+            LinuxSllPacketType::OUTGOING,
+            LinuxSllPacketType::LOOPBACK,
+            LinuxSllPacketType::USER,
+            LinuxSllPacketType::KERNEL,
+        ];
+
+        // clone
+        for v in values {
+            assert_eq!(v, &v.clone());
+        }
+
+        // eq
+        for (a_pos, a) in values.iter().enumerate() {
+            for (b_pos, b) in values.iter().enumerate() {
+                assert_eq!(a_pos == b_pos, a == b);
+                assert_eq!(a_pos != b_pos, a != b);
+            }
+        }
+    }
+}
diff --git a/src/link/linux_sll_payload_slice.rs b/src/link/linux_sll_payload_slice.rs
new file mode 100644
index 0000000..3c39c2a
--- /dev/null
+++ b/src/link/linux_sll_payload_slice.rs
@@ -0,0 +1,70 @@
+use crate::{EtherPayloadSlice, EtherType, LinuxSllProtocolType};
+
+/// Payload of Linux Cooked Capture v1 (SLL) packet
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct LinuxSllPayloadSlice<'a> {
+    /// Identifying content of the payload.
+    pub protocol_type: LinuxSllProtocolType,
+
+    /// Payload
+    pub payload: &'a [u8],
+}
+
+impl<'a> From<EtherPayloadSlice<'a>> for LinuxSllPayloadSlice<'a> {
+    fn from(value: EtherPayloadSlice<'a>) -> LinuxSllPayloadSlice<'a> {
+        LinuxSllPayloadSlice {
+            protocol_type: LinuxSllProtocolType::EtherType(value.ether_type),
+            payload: value.payload,
+        }
+    }
+}
+
+impl<'a> TryFrom<LinuxSllPayloadSlice<'a>> for EtherPayloadSlice<'a> {
+    type Error = ();
+
+    fn try_from(value: LinuxSllPayloadSlice<'a>) -> Result<EtherPayloadSlice<'a>, Self::Error> {
+        match value.protocol_type {
+            LinuxSllProtocolType::LinuxNonstandardEtherType(nonstandard_ether_type) => {
+                Ok(EtherPayloadSlice {
+                    ether_type: EtherType(nonstandard_ether_type.into()),
+                    payload: value.payload,
+                })
+            }
+            LinuxSllProtocolType::EtherType(ether_type) => Ok(EtherPayloadSlice {
+                ether_type,
+                payload: value.payload,
+            }),
+            _ => Err(()),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        let s = LinuxSllPayloadSlice {
+            protocol_type: LinuxSllProtocolType::EtherType(EtherType::IPV4),
+            payload: &[],
+        };
+        assert_eq!(
+            format!(
+                "LinuxSllPayloadSlice {{ protocol_type: {:?}, payload: {:?} }}",
+                s.protocol_type, s.payload
+            ),
+            format!("{:?}", s)
+        );
+    }
+
+    #[test]
+    fn clone_eq() {
+        let s = LinuxSllPayloadSlice {
+            protocol_type: LinuxSllProtocolType::EtherType(EtherType::IPV4),
+            payload: &[],
+        };
+        assert_eq!(s.clone(), s);
+    }
+}
diff --git a/src/link/linux_sll_protocol_type.rs b/src/link/linux_sll_protocol_type.rs
new file mode 100644
index 0000000..da259b6
--- /dev/null
+++ b/src/link/linux_sll_protocol_type.rs
@@ -0,0 +1,137 @@
+use crate::{err, ArpHardwareId, EtherType, LinuxNonstandardEtherType};
+
+/// Represents the "protcol 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.
+///
+/// You can convert pairs of ArpHardwareId and its associated u16 value with `
+/// LinuxSllProtocolType::try_from()`, an Err(_) is returned if the relation is
+/// not defined or known.
+///
+/// ```
+/// use etherparse::LinuxNonstandardEtherType;
+///
+/// // Convert to LinuxNonstandardEtherType using the from & into trait
+/// let link_type: LinuxNonstandardEtherType = 0x0001.try_into().unwrap();
+/// assert_eq!(LinuxNonstandardEtherType::N802_3, link_type);
+///
+/// // convert to u16 using the from & into trait
+/// let num: u16 = LinuxNonstandardEtherType::N802_3.try_into().unwrap();
+/// assert_eq!(0x0001, num);
+/// ```
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum LinuxSllProtocolType {
+    /// The protocol type should be ignored
+    Ignored(u16),
+    /// Netlink protocol type of the encapsulated payload
+    NetlinkProtocolType(u16),
+    /// Generic Routing Encapsulation protocol type for the encapsulated payload
+    GenericRoutingEncapsulationProtocolType(u16),
+    /// EtherType of the encapsulated payload
+    EtherType(EtherType),
+    /// Non-standard ether types of the encapsulated payload
+    LinuxNonstandardEtherType(LinuxNonstandardEtherType),
+}
+
+impl LinuxSllProtocolType {
+    pub const SUPPORTED_ARPHWD: [ArpHardwareId; 5] = [
+        ArpHardwareId::NETLINK,
+        ArpHardwareId::IPGRE,
+        ArpHardwareId::IEEE80211_RADIOTAP,
+        ArpHardwareId::FRAD,
+        ArpHardwareId::ETHER,
+    ];
+
+    pub fn change_value(&mut self, value: u16) {
+        *self = match *self {
+            LinuxSllProtocolType::Ignored(_) => LinuxSllProtocolType::Ignored(value),
+            LinuxSllProtocolType::NetlinkProtocolType(_) => {
+                LinuxSllProtocolType::NetlinkProtocolType(value)
+            }
+            LinuxSllProtocolType::GenericRoutingEncapsulationProtocolType(_) => {
+                LinuxSllProtocolType::GenericRoutingEncapsulationProtocolType(value)
+            }
+            LinuxSllProtocolType::EtherType(_)
+            | LinuxSllProtocolType::LinuxNonstandardEtherType(_) => {
+                match LinuxNonstandardEtherType::try_from(value) {
+                    Ok(v) => LinuxSllProtocolType::LinuxNonstandardEtherType(v),
+                    Err(_) => LinuxSllProtocolType::EtherType(EtherType(value)),
+                }
+            }
+        }
+    }
+}
+
+impl TryFrom<(ArpHardwareId, u16)> for LinuxSllProtocolType {
+    type Error = err::linux_sll::HeaderError;
+
+    fn try_from(
+        (arp_hardware_id, protocol_type): (ArpHardwareId, u16),
+    ) -> Result<Self, Self::Error> {
+        match arp_hardware_id {
+            ArpHardwareId::NETLINK => Ok(LinuxSllProtocolType::NetlinkProtocolType(protocol_type)),
+            ArpHardwareId::IPGRE => {
+                Ok(LinuxSllProtocolType::GenericRoutingEncapsulationProtocolType(protocol_type))
+            }
+            ArpHardwareId::IEEE80211_RADIOTAP => Ok(LinuxSllProtocolType::Ignored(protocol_type)),
+            ArpHardwareId::FRAD => Ok(LinuxSllProtocolType::Ignored(protocol_type)),
+            ArpHardwareId::ETHER => match LinuxNonstandardEtherType::try_from(protocol_type) {
+                Ok(v) => Ok(LinuxSllProtocolType::LinuxNonstandardEtherType(v)),
+                Err(_) => Ok(LinuxSllProtocolType::EtherType(EtherType(protocol_type))),
+            },
+            _ => Err(err::linux_sll::HeaderError::UnsupportedArpHardwareId {
+                arp_hardware_type: arp_hardware_id,
+            }),
+        }
+    }
+}
+
+impl From<LinuxSllProtocolType> for u16 {
+    fn from(value: LinuxSllProtocolType) -> u16 {
+        match value {
+            LinuxSllProtocolType::Ignored(value) => value,
+            LinuxSllProtocolType::NetlinkProtocolType(value) => value,
+            LinuxSllProtocolType::GenericRoutingEncapsulationProtocolType(value) => value,
+            LinuxSllProtocolType::EtherType(value) => value.into(),
+            LinuxSllProtocolType::LinuxNonstandardEtherType(value) => value.into(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn try_from_pair_arp_hardware_id_u16() {
+        assert_eq!(
+            LinuxSllProtocolType::try_from((ArpHardwareId::NETLINK, 123)),
+            Ok(LinuxSllProtocolType::NetlinkProtocolType(123))
+        );
+        assert_eq!(
+            LinuxSllProtocolType::try_from((ArpHardwareId::IPGRE, 123)),
+            Ok(LinuxSllProtocolType::GenericRoutingEncapsulationProtocolType(123))
+        );
+        assert_eq!(
+            LinuxSllProtocolType::try_from((ArpHardwareId::IEEE80211_RADIOTAP, 123)),
+            Ok(LinuxSllProtocolType::Ignored(123))
+        );
+        assert_eq!(
+            LinuxSllProtocolType::try_from((ArpHardwareId::FRAD, 123)),
+            Ok(LinuxSllProtocolType::Ignored(123))
+        );
+        assert_eq!(
+            LinuxSllProtocolType::try_from((
+                ArpHardwareId::ETHER,
+                u16::from(LinuxNonstandardEtherType::N802_3)
+            )),
+            Ok(LinuxSllProtocolType::LinuxNonstandardEtherType(
+                LinuxNonstandardEtherType::N802_3
+            ))
+        );
+        assert_eq!(
+            LinuxSllProtocolType::try_from((ArpHardwareId::ETHER, u16::from(EtherType::IPV4))),
+            Ok(LinuxSllProtocolType::EtherType(EtherType::IPV4))
+        );
+    }
+}
diff --git a/src/link/linux_sll_slice.rs b/src/link/linux_sll_slice.rs
new file mode 100644
index 0000000..9c2f219
--- /dev/null
+++ b/src/link/linux_sll_slice.rs
@@ -0,0 +1,255 @@
+use crate::{
+    err::{self, Layer},
+    ArpHardwareId, LenSource, LinuxSllHeader, LinuxSllHeaderSlice, LinuxSllPacketType,
+    LinuxSllPayloadSlice, LinuxSllProtocolType,
+};
+
+/// Slice containing a Linux Cooked Capture v1 (SLL) header & payload.
+#[derive(Clone, Eq, PartialEq)]
+pub struct LinuxSllSlice<'a> {
+    header_slice: LinuxSllHeaderSlice<'a>,
+    header_and_payload_slice: &'a [u8],
+}
+
+impl<'a> LinuxSllSlice<'a> {
+    /// Try creating a [`LinuxSllSlice`] from a slice containing the
+    /// header & payload
+    pub fn from_slice(
+        slice: &'a [u8],
+    ) -> Result<LinuxSllSlice<'a>, err::linux_sll::HeaderSliceError> {
+        // check length
+        if slice.len() < LinuxSllHeader::LEN {
+            return Err(err::linux_sll::HeaderSliceError::Len(err::LenError {
+                required_len: LinuxSllHeader::LEN,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: Layer::LinuxSllHeader,
+                layer_start_offset: 0,
+            }));
+        }
+
+        // extract header
+        match LinuxSllHeaderSlice::from_slice(&slice[0..LinuxSllHeader::LEN]) {
+            Err(err) => Err(err),
+            Ok(header_slice) => Ok(LinuxSllSlice {
+                header_slice,
+                header_and_payload_slice: slice,
+            }),
+        }
+    }
+
+    /// Returns the slice containing the Linux Cooked Capture v1 (SLL) header &
+    /// payload.
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.header_and_payload_slice
+    }
+
+    /// Read the packet type field from the header
+    #[inline]
+    pub fn packet_type(&self) -> LinuxSllPacketType {
+        self.header_slice.packet_type()
+    }
+
+    /// Read the arp hardware type field from the header
+    #[inline]
+    pub fn arp_hardware_type(&self) -> ArpHardwareId {
+        self.header_slice.arp_hardware_type()
+    }
+
+    /// Read the link layer address length field from the header
+    #[inline]
+    pub fn sender_address_valid_length(&self) -> u16 {
+        self.header_slice.sender_address_valid_length()
+    }
+
+    /// Read the link layer address field from the header. Only the first
+    /// `LinuxSllSlice::link_layer_address_length` bytes are meaningful
+    #[inline]
+    pub fn sender_address_full(&self) -> [u8; 8] {
+        self.header_slice.sender_address_full()
+    }
+
+    /// Get the meaningful bytes of the slice of the link layer address from
+    /// the header
+    #[inline]
+    pub fn sender_address(&self) -> &'a [u8] {
+        self.header_slice.sender_address()
+    }
+
+    /// Read the protocol type field from the header
+    #[inline]
+    pub fn protocol_type(&self) -> LinuxSllProtocolType {
+        self.header_slice.protocol_type()
+    }
+
+    /// Decode all the header fields and copy the results to a
+    /// [`LinuxSllHeader`] struct
+    pub fn to_header(&self) -> LinuxSllHeader {
+        LinuxSllHeader {
+            packet_type: self.packet_type(),
+            arp_hrd_type: self.arp_hardware_type(),
+            sender_address_valid_length: self.sender_address_valid_length(),
+            sender_address: self.sender_address_full(),
+            protocol_type: self.protocol_type(),
+        }
+    }
+
+    /// Slice only containing the header
+    pub fn header_slice(&self) -> &[u8] {
+        self.header_slice.slice()
+    }
+
+    /// Returns the slice containing the Ethernet II payload & ether type
+    /// identifying it's content type.
+    #[inline]
+    pub fn payload(&self) -> LinuxSllPayloadSlice<'a> {
+        LinuxSllPayloadSlice {
+            protocol_type: self.protocol_type(),
+            payload: self.payload_slice(),
+        }
+    }
+
+    /// Slice only containing the payload
+    #[inline]
+    pub fn payload_slice(&self) -> &'a [u8] {
+        // SAFETY: Safe as the slice length was verified to be at least
+        // LinuxSllHeader::LEN by "from_slice".
+        unsafe {
+            core::slice::from_raw_parts(
+                self.header_and_payload_slice
+                    .as_ptr()
+                    .add(LinuxSllHeader::LEN),
+                self.header_and_payload_slice.len() - LinuxSllHeader::LEN,
+            )
+        }
+    }
+
+    /// Length of the header in bytes (equal to [`crate::LinuxSllHeader::LEN`])
+    #[inline]
+    pub const fn header_len(&self) -> usize {
+        LinuxSllHeader::LEN
+    }
+}
+
+impl<'a> core::fmt::Debug for LinuxSllSlice<'a> {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        f.debug_struct("LinuxSllSlice")
+            .field("header", &self.to_header())
+            .field("payload", &self.payload())
+            .finish()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn debug_clone_eq(
+            linux_sll in linux_sll_any()
+        ) {
+            let payload: [u8;8] = [1,2,3,4,5,6,7,8];
+            let mut data = Vec::with_capacity(
+                linux_sll.header_len() +
+                payload.len()
+            );
+            data.extend_from_slice(&linux_sll.to_bytes());
+            data.extend_from_slice(&payload);
+
+            // decode packet
+            let slice = LinuxSllSlice::from_slice(&data).unwrap();
+
+            // check debug output
+            prop_assert_eq!(
+                format!("{:?}", slice),
+                format!(
+                    "LinuxSllSlice {{ header: {:?}, payload: {:?} }}",
+                    slice.to_header(),
+                    slice.payload(),
+                )
+            );
+            prop_assert_eq!(slice.clone(), slice);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn getters(linux_sll in linux_sll_any()) {
+            let payload: [u8;8] = [1,2,3,4,5,6,7,8];
+            let mut data = Vec::with_capacity(
+                linux_sll.header_len() +
+                payload.len()
+            );
+            data.extend_from_slice(&linux_sll.to_bytes());
+            data.extend_from_slice(&payload);
+
+            let slice = LinuxSllSlice::from_slice(&data).unwrap();
+            assert_eq!(linux_sll.packet_type, slice.packet_type());
+            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.protocol_type, slice.protocol_type());
+            assert_eq!(&payload, slice.payload_slice());
+            assert_eq!(
+                LinuxSllPayloadSlice{
+                    payload: &payload,
+                    protocol_type: linux_sll.protocol_type,
+                },
+                slice.payload()
+            );
+            assert_eq!(linux_sll, slice.to_header());
+            assert_eq!(&data, slice.slice());
+            assert_eq!(&data[..LinuxSllHeader::LEN], slice.header_slice());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(linux_sll in linux_sll_any()) {
+
+            let payload: [u8;10] = [1,2,3,4,5,6,7,8,9,10];
+            let data = {
+                let mut data = Vec::with_capacity(
+                    linux_sll.header_len() +
+                    payload.len()
+                );
+                data.extend_from_slice(&linux_sll.to_bytes());
+                data.extend_from_slice(&payload);
+                data
+            };
+
+            // normal decode
+            {
+                let slice = LinuxSllSlice::from_slice(&data).unwrap();
+                assert_eq!(slice.to_header(), linux_sll);
+                assert_eq!(slice.payload_slice(), &payload);
+            }
+
+            // decode without payload
+            {
+                let slice = LinuxSllSlice::from_slice(&data[..LinuxSllHeader::LEN]).unwrap();
+                assert_eq!(slice.to_header(), linux_sll);
+                assert_eq!(slice.payload_slice(), &[]);
+            }
+
+            // length error
+            for len in 0..LinuxSllHeader::LEN {
+                assert_eq!(
+                    LinuxSllSlice::from_slice(&data[..len]).unwrap_err(),
+                    err::linux_sll::HeaderSliceError::Len(err::LenError{
+                        required_len: LinuxSllHeader::LEN,
+                        len,
+                        len_source: LenSource::Slice,
+                        layer: Layer::LinuxSllHeader,
+                        layer_start_offset: 0
+                    })
+                );
+            }
+        }
+    }
+}
diff --git a/src/link/mod.rs b/src/link/mod.rs
new file mode 100644
index 0000000..fab5b80
--- /dev/null
+++ b/src/link/mod.rs
@@ -0,0 +1,25 @@
+pub mod arp_hardware_id;
+pub mod double_vlan_header;
+pub mod double_vlan_header_slice;
+pub mod double_vlan_slice;
+pub mod ether_payload_slice;
+pub mod ether_type_impl;
+pub mod ethernet2_header;
+pub mod ethernet2_header_slice;
+pub mod ethernet2_slice;
+pub mod link_header;
+pub mod link_slice;
+pub mod linux_nonstandard_ether_type;
+pub mod linux_sll_header;
+pub mod linux_sll_header_slice;
+pub mod linux_sll_packet_type;
+pub mod linux_sll_payload_slice;
+pub mod linux_sll_protocol_type;
+pub mod linux_sll_slice;
+pub mod single_vlan_header;
+pub mod single_vlan_header_slice;
+pub mod single_vlan_slice;
+pub mod vlan_header;
+pub mod vlan_id;
+pub mod vlan_pcp;
+pub mod vlan_slice;
diff --git a/src/link/single_vlan_header.rs b/src/link/single_vlan_header.rs
new file mode 100644
index 0000000..a8d9ce0
--- /dev/null
+++ b/src/link/single_vlan_header.rs
@@ -0,0 +1,280 @@
+use crate::*;
+
+/// IEEE 802.1Q VLAN Tagging Header
+#[derive(Clone, Debug, Eq, PartialEq, Default)]
+pub struct SingleVlanHeader {
+    /// A 3 bit number which refers to the IEEE 802.1p class of service and maps to the frame priority level.
+    pub pcp: VlanPcp,
+    /// Indicate that the frame may be dropped under the presence of congestion.
+    pub drop_eligible_indicator: bool,
+    /// 12 bits vland identifier.
+    pub vlan_id: VlanId,
+    /// "Tag protocol identifier": Type id of content after this header. Refer to the "EtherType" for a list of possible supported values.
+    pub ether_type: EtherType,
+}
+
+impl SingleVlanHeader {
+    /// Serialized size of an VLAN header in bytes/octets.
+    pub const LEN: usize = 4;
+
+    #[deprecated(since = "0.14.0", note = "Use `SingleVlanHeader::LEN` instead")]
+    pub const SERIALIZED_SIZE: usize = SingleVlanHeader::LEN;
+
+    /// Read an SingleVlanHeader 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<(SingleVlanHeader, &[u8]), err::LenError> {
+        SingleVlanHeader::from_slice(slice)
+    }
+
+    /// Read an SingleVlanHeader from a slice and return the header & unused parts of the slice.
+    #[inline]
+    pub fn from_slice(slice: &[u8]) -> Result<(SingleVlanHeader, &[u8]), err::LenError> {
+        Ok((
+            SingleVlanHeaderSlice::from_slice(slice)?.to_header(),
+            &slice[SingleVlanHeader::LEN..],
+        ))
+    }
+
+    /// Read an SingleVlanHeader from a static sized byte array.
+    #[inline]
+    pub fn from_bytes(bytes: [u8; 4]) -> SingleVlanHeader {
+        SingleVlanHeader {
+            pcp: unsafe {
+                // SAFETY: Safe as bitmasks guarantee that value does not exceed
+                //         0b0000_0111.
+                VlanPcp::new_unchecked((bytes[0] >> 5) & 0b0000_0111u8)
+            },
+            drop_eligible_indicator: 0 != (bytes[0] & 0b0001_0000u8),
+            vlan_id: unsafe {
+                // SAFETY: Safe as bitmasks guarantee that value does not exceed
+                //         0b0000_1111_1111_1111.
+                VlanId::new_unchecked(u16::from_be_bytes([bytes[0] & 0b0000_1111u8, bytes[1]]))
+            },
+            ether_type: EtherType(u16::from_be_bytes([bytes[2], bytes[3]])),
+        }
+    }
+
+    /// Read a IEEE 802.1Q VLAN tagging header
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut T,
+    ) -> Result<SingleVlanHeader, std::io::Error> {
+        let buffer = {
+            let mut buffer: [u8; SingleVlanHeader::LEN] = [0; SingleVlanHeader::LEN];
+            reader.read_exact(&mut buffer)?;
+            buffer
+        };
+
+        Ok(
+            // SAFETY: Safe as the buffer has the exact size of an vlan header.
+            unsafe { SingleVlanHeaderSlice::from_slice_unchecked(&buffer) }.to_header(),
+        )
+    }
+
+    /// Write the IEEE 802.1Q VLAN tagging header
+    #[inline]
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
+        writer.write_all(&self.to_bytes())
+    }
+
+    /// Length of the serialized header in bytes.
+    #[inline]
+    pub fn header_len(&self) -> usize {
+        4
+    }
+
+    /// Returns the serialized form of the header or an value error in case
+    /// the header values are outside of range.
+    #[inline]
+    pub fn to_bytes(&self) -> [u8; 4] {
+        let id_be = self.vlan_id.value().to_be_bytes();
+        let eth_type_be = self.ether_type.0.to_be_bytes();
+        [
+            (if self.drop_eligible_indicator {
+                id_be[0] | 0x10
+            } else {
+                id_be[0]
+            } | (self.pcp.value() << 5)),
+            id_be[1],
+            eth_type_be[0],
+            eth_type_be[1],
+        ]
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{test_gens::*, *};
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+    use std::io::{Cursor, ErrorKind};
+
+    #[test]
+    fn constants() {
+        assert_eq!(4, SingleVlanHeader::LEN);
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            input in vlan_single_any(),
+            dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
+        ) {
+            // serialize
+            let mut buffer: Vec<u8> = Vec::with_capacity(input.header_len() + dummy_data.len());
+            input.write(&mut buffer).unwrap();
+            buffer.extend(&dummy_data[..]);
+
+            // normal
+            {
+                let (result, rest) = SingleVlanHeader::from_slice(&buffer).unwrap();
+                assert_eq!(result, input);
+                assert_eq!(rest, &buffer[4..]);
+            }
+            #[allow(deprecated)]
+            {
+                let (result, rest) = SingleVlanHeader::read_from_slice(&buffer).unwrap();
+                assert_eq!(result, input);
+                assert_eq!(rest, &buffer[4..]);
+            }
+
+            // slice length to small
+            for len in 0..4 {
+                assert_eq!(
+                    SingleVlanHeader::from_slice(&buffer[..len])
+                        .unwrap_err(),
+                    err::LenError{
+                        required_len: 4,
+                        len: len,
+                        len_source: LenSource::Slice,
+                        layer:  err::Layer::VlanHeader,
+                        layer_start_offset: 0,
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_bytes(input in vlan_single_any()) {
+            let actual = SingleVlanHeader::from_bytes(
+                input.to_bytes()
+            );
+            assert_eq!(actual, input);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read(
+            input in vlan_single_any(),
+            dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
+        ) {
+            // serialize
+            let mut buffer: Vec<u8> = 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 = SingleVlanHeader::read(&mut cursor).unwrap();
+                assert_eq!(result, input);
+                assert_eq!(4, cursor.position());
+            }
+
+            // unexpexted eof
+            for len in 0..4 {
+                let mut cursor = Cursor::new(&buffer[0..len]);
+                assert_eq!(
+                    SingleVlanHeader::read(&mut cursor)
+                    .unwrap_err()
+                    .kind(),
+                    ErrorKind::UnexpectedEof
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write_and_to_bytes(input in vlan_single_any()) {
+            // normal write
+            {
+                let mut buffer: Vec<u8> = Vec::with_capacity(input.header_len());
+                input.write(&mut buffer).unwrap();
+                assert_eq!(&buffer[..], &input.to_bytes());
+                {
+                    let id_be = input.vlan_id.value().to_be_bytes();
+                    let eth_type_be = input.ether_type.0.to_be_bytes();
+                    assert_eq!(
+                        input.to_bytes(),
+                        [
+                            (
+                                id_be[0] | if input.drop_eligible_indicator {
+                                    0x10
+                                } else {
+                                    0
+                                } | (input.pcp.value() << 5)
+                            ),
+                            id_be[1],
+                            eth_type_be[0],
+                            eth_type_be[1]
+                        ]
+                    );
+                }
+            }
+
+            // unexpected eof
+            for len in 0..4 {
+                let mut buffer = [0u8;4];
+                let mut cursor = Cursor::new(&mut buffer[..len]);
+                assert!(input.write(&mut cursor).is_err());
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header_len(input in vlan_single_any()) {
+            assert_eq!(4, input.header_len());
+        }
+    }
+
+    #[test]
+    fn default() {
+        let actual: SingleVlanHeader = Default::default();
+        assert_eq!(0, actual.pcp.value());
+        assert_eq!(false, actual.drop_eligible_indicator);
+        assert_eq!(0, actual.vlan_id.value());
+        assert_eq!(0, actual.ether_type.0);
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(input in vlan_single_any()) {
+            assert_eq!(input, input.clone());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn dbg(input in vlan_single_any()) {
+            assert_eq!(
+                &format!(
+                    "SingleVlanHeader {{ pcp: {:?}, drop_eligible_indicator: {}, vlan_id: {:?}, ether_type: {:?} }}",
+                    input.pcp,
+                    input.drop_eligible_indicator,
+                    input.vlan_id,
+                    input.ether_type,
+                ),
+                &format!("{:?}", input)
+            );
+        }
+    }
+}
diff --git a/src/link/single_vlan_header_slice.rs b/src/link/single_vlan_header_slice.rs
new file mode 100644
index 0000000..2406cc6
--- /dev/null
+++ b/src/link/single_vlan_header_slice.rs
@@ -0,0 +1,195 @@
+use crate::*;
+use core::slice::from_raw_parts;
+
+///A slice containing a single vlan header of a network package.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct SingleVlanHeaderSlice<'a> {
+    slice: &'a [u8],
+}
+
+impl<'a> SingleVlanHeaderSlice<'a> {
+    ///Creates a vlan header slice from a slice.
+    #[inline]
+    pub fn from_slice(slice: &'a [u8]) -> Result<SingleVlanHeaderSlice<'a>, err::LenError> {
+        //check length
+        if slice.len() < SingleVlanHeader::LEN {
+            return Err(err::LenError {
+                required_len: SingleVlanHeader::LEN,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::VlanHeader,
+                layer_start_offset: 0,
+            });
+        }
+
+        //all done
+        Ok(SingleVlanHeaderSlice::<'a> {
+            // SAFETY:
+            // Safe as the slice length is checked beforehand to have
+            // at least the length of SingleVlanHeader::LEN (4)
+            slice: unsafe { from_raw_parts(slice.as_ptr(), SingleVlanHeader::LEN) },
+        })
+    }
+
+    /// Converts the given slice into a vlan header slice WITHOUT any
+    /// checks to ensure that the data present is an vlan header or that the
+    /// slice length is matching the header length.
+    ///
+    /// If you are not sure what this means, use [`SingleVlanHeaderSlice::from_slice`]
+    /// instead.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensured that the given slice has the length of
+    /// [`SingleVlanHeader::LEN`]
+    #[inline]
+    pub(crate) unsafe fn from_slice_unchecked(slice: &[u8]) -> SingleVlanHeaderSlice {
+        SingleVlanHeaderSlice { slice }
+    }
+
+    /// Returns the slice containing the single vlan header
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+
+    /// Read the "priority_code_point" field from the slice. This is a 3 bit number which refers to the IEEE 802.1p class of service and maps to the frame priority level.
+    #[inline]
+    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.
+            VlanPcp::new_unchecked((*self.slice.get_unchecked(0) >> 5) & 0b0000_0111)
+        }
+    }
+
+    /// Read the "drop_eligible_indicator" flag from the slice. Indicates that the frame may be dropped under the presence of congestion.
+    #[inline]
+    pub fn drop_eligible_indicator(&self) -> bool {
+        // SAFETY:
+        // Slice len checked in constructor to be at least 4.
+        unsafe { 0 != (*self.slice.get_unchecked(0) & 0x10) }
+    }
+
+    /// Reads the 12 bits "vland identifier" field from the slice.
+    #[inline]
+    pub fn vlan_identifier(&self) -> VlanId {
+        // SAFETY:
+        // Slice len checked in constructor to be at least 4 &
+        // value and the value is guaranteed not to exceed
+        // 0b0000_1111_1111_1111 as the upper bits have been
+        // bitmasked out.
+        unsafe {
+            VlanId::new_unchecked(u16::from_be_bytes([
+                *self.slice.get_unchecked(0) & 0b0000_1111,
+                *self.slice.get_unchecked(1),
+            ]))
+        }
+    }
+
+    /// Read the "Tag protocol identifier" field from the slice. Refer to the "EtherType" for a list of possible supported values.
+    #[inline]
+    pub fn ether_type(&self) -> EtherType {
+        // SAFETY:
+        // Slice len checked in constructor to be at least 4.
+        EtherType(unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) })
+    }
+
+    /// Decode all the fields and copy the results to a SingleVlanHeader struct
+    #[inline]
+    pub fn to_header(&self) -> SingleVlanHeader {
+        SingleVlanHeader {
+            pcp: self.priority_code_point(),
+            drop_eligible_indicator: self.drop_eligible_indicator(),
+            vlan_id: self.vlan_identifier(),
+            ether_type: self.ether_type(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{test_gens::*, *};
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            input in vlan_single_any(),
+            dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
+        ) {
+            // serialize
+            let mut buffer: Vec<u8> = Vec::with_capacity(input.header_len() + dummy_data.len());
+            input.write(&mut buffer).unwrap();
+            buffer.extend(&dummy_data[..]);
+
+            // normal
+            {
+                let slice = SingleVlanHeaderSlice::from_slice(&buffer).unwrap();
+                assert_eq!(slice.slice(), &buffer[..4]);
+            }
+
+            // slice length to small
+            for len in 0..4 {
+                assert_eq!(
+                    SingleVlanHeaderSlice::from_slice(&buffer[..len])
+                        .unwrap_err(),
+                    err::LenError{
+                        required_len: 4,
+                        len: len,
+                        len_source: LenSource::Slice,
+                        layer:  err::Layer::VlanHeader,
+                        layer_start_offset: 0,
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn getters(input in vlan_single_any()) {
+            let bytes = input.to_bytes();
+            let slice = SingleVlanHeaderSlice::from_slice(&bytes).unwrap();
+
+            assert_eq!(input.pcp, slice.priority_code_point());
+            assert_eq!(input.drop_eligible_indicator, slice.drop_eligible_indicator());
+            assert_eq!(input.vlan_id, slice.vlan_identifier());
+            assert_eq!(input.ether_type, slice.ether_type());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_header(input in vlan_single_any()) {
+            let bytes = input.to_bytes();
+            let slice = SingleVlanHeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(input, slice.to_header());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(input in vlan_single_any()) {
+            let bytes = input.to_bytes();
+            let slice = SingleVlanHeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(slice, slice.clone());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn dbg(input in vlan_single_any()) {
+            let bytes = input.to_bytes();
+            let slice = SingleVlanHeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(
+                &format!(
+                    "SingleVlanHeaderSlice {{ slice: {:?} }}",
+                    slice.slice(),
+                ),
+                &format!("{:?}", slice)
+            );
+        }
+    }
+}
diff --git a/src/link/single_vlan_slice.rs b/src/link/single_vlan_slice.rs
new file mode 100644
index 0000000..ca3ce29
--- /dev/null
+++ b/src/link/single_vlan_slice.rs
@@ -0,0 +1,250 @@
+use crate::{err::*, *};
+
+/// Slice containing a VLAN header & payload.
+#[derive(Clone, Eq, PartialEq)]
+pub struct SingleVlanSlice<'a> {
+    pub(crate) slice: &'a [u8],
+}
+
+impl<'a> SingleVlanSlice<'a> {
+    /// Try creating a [`SingleVlanSlice`] from a slice containing the
+    /// VLAN header & payload.
+    pub fn from_slice(slice: &'a [u8]) -> Result<SingleVlanSlice<'a>, LenError> {
+        // check length
+        if slice.len() < SingleVlanHeader::LEN {
+            return Err(err::LenError {
+                required_len: SingleVlanHeader::LEN,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::VlanHeader,
+                layer_start_offset: 0,
+            });
+        }
+
+        Ok(SingleVlanSlice { slice })
+    }
+
+    /// Returns the slice containing the VLAN header and payload.
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+
+    /// Read the "priority_code_point" field of the VLAN header.
+    ///
+    /// This is a 3 bit number which refers to the IEEE 802.1p class
+    /// of service and maps to the frame priority level.
+    #[inline]
+    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.
+            VlanPcp::new_unchecked((*self.slice.get_unchecked(0) >> 5) & 0b0000_0111)
+        }
+    }
+
+    /// Read the "drop_eligible_indicator" flag of the VLAN header.
+    ///
+    /// Indicates that the frame may be dropped under the presence
+    /// of congestion.
+    #[inline]
+    pub fn drop_eligible_indicator(&self) -> bool {
+        // SAFETY:
+        // Slice len checked in constructor to be at least 4.
+        unsafe { 0 != (*self.slice.get_unchecked(0) & 0x10) }
+    }
+
+    /// Reads the 12 bits "vland 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
+        // 0b0000_1111_1111_1111 as the upper bits have been
+        // bitmasked out.
+        unsafe {
+            VlanId::new_unchecked(u16::from_be_bytes([
+                *self.slice.get_unchecked(0) & 0b0000_1111,
+                *self.slice.get_unchecked(1),
+            ]))
+        }
+    }
+
+    /// Read the "Tag protocol identifier" field from the VLAN header.
+    ///
+    /// Refer to the "EtherType" for a list of possible supported values.
+    #[inline]
+    pub fn ether_type(&self) -> EtherType {
+        // SAFETY:
+        // Slice len checked in constructor to be at least 4.
+        EtherType(unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) })
+    }
+
+    /// Decode all the fields and copy the results to a SingleVlanHeader struct
+    #[inline]
+    pub fn to_header(&self) -> SingleVlanHeader {
+        SingleVlanHeader {
+            pcp: self.priority_code_point(),
+            drop_eligible_indicator: self.drop_eligible_indicator(),
+            vlan_id: self.vlan_identifier(),
+            ether_type: self.ether_type(),
+        }
+    }
+
+    /// Slice containing the Ethernet 2 header.
+    pub fn header_slice(&self) -> &[u8] {
+        unsafe {
+            // SAFETY:
+            // Safe as the contructor checks that the slice has
+            // at least the length of SingleVlanHeader::LEN (4).
+            core::slice::from_raw_parts(self.slice.as_ptr(), SingleVlanHeader::LEN)
+        }
+    }
+
+    /// Returns the slice containing the VLAN payload & ether type
+    /// identifying it's content type.
+    #[inline]
+    pub fn payload(&self) -> EtherPayloadSlice<'a> {
+        EtherPayloadSlice {
+            ether_type: self.ether_type(),
+            payload: self.payload_slice(),
+        }
+    }
+
+    /// Returns the slice containing the VLAN payload.
+    #[inline]
+    pub fn payload_slice(&self) -> &'a [u8] {
+        unsafe {
+            // SAFETY:
+            // Safe as the contructor 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),
+                self.slice.len() - SingleVlanHeader::LEN,
+            )
+        }
+    }
+
+    /// Length of the VLAN header in bytes (equal to
+    /// [`crate::SingleVlanHeader::LEN`]).
+    #[inline]
+    pub const fn header_len(&self) -> usize {
+        SingleVlanHeader::LEN
+    }
+}
+
+impl<'a> core::fmt::Debug for SingleVlanSlice<'a> {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        f.debug_struct("SingleVlanSlice")
+            .field("header", &self.to_header())
+            .field("payload", &self.payload())
+            .finish()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn debug_clone_eq(
+            vlan in vlan_single_any()
+        ) {
+            let payload: [u8;8] = [1,2,3,4,5,6,7,8];
+            let mut data = Vec::with_capacity(
+                vlan.header_len() +
+                payload.len()
+            );
+            data.extend_from_slice(&vlan.to_bytes());
+            data.extend_from_slice(&payload);
+
+            // decode packet
+            let slice = SingleVlanSlice::from_slice(&data).unwrap();
+
+            // check debug output
+            prop_assert_eq!(
+                format!("{:?}", slice),
+                format!(
+                    "SingleVlanSlice {{ header: {:?}, payload: {:?} }}",
+                    slice.to_header(),
+                    slice.payload(),
+                )
+            );
+            prop_assert_eq!(slice.clone(), slice);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn getters(vlan in vlan_single_any()) {
+            let payload: [u8;8] = [1,2,3,4,5,6,7,8];
+            let mut data = Vec::with_capacity(
+                vlan.header_len() +
+                payload.len()
+            );
+            data.extend_from_slice(&vlan.to_bytes());
+            data.extend_from_slice(&payload);
+
+            let slice = SingleVlanSlice::from_slice(&data).unwrap();
+            assert_eq!(&data, slice.slice());
+            assert_eq!(vlan.pcp, slice.priority_code_point());
+            assert_eq!(vlan.drop_eligible_indicator, slice.drop_eligible_indicator());
+            assert_eq!(vlan.vlan_id, slice.vlan_identifier());
+            assert_eq!(vlan.ether_type, slice.ether_type());
+            assert_eq!(vlan, slice.to_header());
+            assert_eq!(&data[..SingleVlanHeader::LEN], slice.header_slice());
+
+            assert_eq!(
+                EtherPayloadSlice {
+                    ether_type: vlan.ether_type,
+                    payload: &data[SingleVlanHeader::LEN..],
+                },
+                slice.payload()
+            );
+            assert_eq!(&data[SingleVlanHeader::LEN..], slice.payload_slice());
+            assert_eq!(SingleVlanHeader::LEN, slice.header_len());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(vlan in vlan_single_any()) {
+
+            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 = SingleVlanSlice::from_slice(&data).unwrap();
+                assert_eq!(slice.to_header(), vlan);
+                assert_eq!(slice.payload_slice(), &payload);
+            }
+
+            // length error
+            for len in 0..SingleVlanHeader::LEN {
+                assert_eq!(
+                    SingleVlanSlice::from_slice(&data[..len]).unwrap_err(),
+                    LenError{
+                        required_len: SingleVlanHeader::LEN,
+                        len,
+                        len_source: LenSource::Slice,
+                        layer: Layer::VlanHeader,
+                        layer_start_offset: 0
+                    }
+                );
+            }
+        }
+    }
+}
diff --git a/src/link/vlan_header.rs b/src/link/vlan_header.rs
new file mode 100644
index 0000000..f01a197
--- /dev/null
+++ b/src/link/vlan_header.rs
@@ -0,0 +1,195 @@
+use crate::*;
+
+/// IEEE 802.1Q VLAN Tagging Header (can be single or double tagged).
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum VlanHeader {
+    /// IEEE 802.1Q VLAN Tagging Header
+    Single(SingleVlanHeader),
+    /// IEEE 802.1Q double VLAN Tagging Header
+    Double(DoubleVlanHeader),
+}
+
+impl VlanHeader {
+    /// All ether types that identify a vlan header.
+    pub const VLAN_ETHER_TYPES: [EtherType; 3] = [
+        ether_type::VLAN_TAGGED_FRAME,
+        ether_type::PROVIDER_BRIDGING,
+        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<T: std::io::Write + Sized>(&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 {
+        match self {
+            VlanHeader::Single(s) => s.ether_type,
+            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 proptest::prelude::*;
+
+    #[test]
+    fn constants() {
+        use ether_type::*;
+        use VlanHeader as V;
+
+        assert_eq!(3, V::VLAN_ETHER_TYPES.len());
+        assert_eq!(VLAN_TAGGED_FRAME, V::VLAN_ETHER_TYPES[0]);
+        assert_eq!(PROVIDER_BRIDGING, V::VLAN_ETHER_TYPES[1]);
+        assert_eq!(VLAN_DOUBLE_TAGGED_FRAME, V::VLAN_ETHER_TYPES[2]);
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(
+            single in vlan_single_any(),
+            double in vlan_double_any(),
+        ) {
+            // single eq
+            {
+                let value = VlanHeader::Single(single.clone());
+                assert_eq!(value, value.clone());
+            }
+            // double
+            {
+                let value = VlanHeader::Double(double);
+                assert_eq!(value, value.clone());
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn dbg(
+            single in vlan_single_any(),
+            double in vlan_double_any(),
+        ) {
+            // single
+            {
+                let value = VlanHeader::Single(single.clone());
+                assert_eq!(
+                    &format!(
+                        "Single({:?})",
+                        single
+                    ),
+                    &format!("{:?}", value)
+                );
+            }
+            // double
+            {
+                let value = VlanHeader::Double(double.clone());
+                assert_eq!(
+                    &format!(
+                        "Double({:?})",
+                        double
+                    ),
+                    &format!("{:?}", value)
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn next_header(
+            single in vlan_single_any(),
+            double in vlan_double_any(),
+        ) {
+            // single
+            {
+                let value = VlanHeader::Single(single.clone());
+                assert_eq!(value.next_header(), single.ether_type);
+            }
+            // double
+            {
+                let value = VlanHeader::Double(double.clone());
+                assert_eq!(value.next_header(), double.inner.ether_type);
+            }
+        }
+    }
+
+    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/src/link/vlan_id.rs b/src/link/vlan_id.rs
new file mode 100644
index 0000000..d5d5bfd
--- /dev/null
+++ b/src/link/vlan_id.rs
@@ -0,0 +1,252 @@
+use crate::err::ValueTooBigError;
+
+/// 12 bit unsigned integer containing the "VLAN identifier" (present
+/// in the [`crate::SingleVlanHeader`]).
+#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct VlanId(u16);
+
+impl VlanId {
+    /// VlanId with value 0.
+    pub const ZERO: VlanId = VlanId(0);
+
+    /// Maximum value of an VLAN id.
+    pub const MAX_U16: u16 = 0b0000_1111_1111_1111;
+
+    /// Tries to create an [`VlanId`] and checks that the passed value
+    /// is smaller or equal than [`VlanId::MAX_U16`] (12 bit unsigned integer).
+    ///
+    /// In case the passed value is bigger then what can be represented in an 12 bit
+    /// integer an error is returned. Otherwise an `Ok` containing the [`VlanId`].
+    ///
+    /// ```
+    /// use etherparse::VlanId;
+    ///
+    /// let vlanid = VlanId::try_new(2).unwrap();
+    /// assert_eq!(vlanid.value(), 2);
+    ///
+    /// // if a number that can not be represented in an 12 bit integer
+    /// // gets passed in an error is returned
+    /// use etherparse::err::{ValueTooBigError, ValueType};
+    /// assert_eq!(
+    ///     VlanId::try_new(VlanId::MAX_U16 + 1),
+    ///     Err(ValueTooBigError{
+    ///         actual: VlanId::MAX_U16 + 1,
+    ///         max_allowed: VlanId::MAX_U16,
+    ///         value_type: ValueType::VlanId,
+    ///     })
+    /// );
+    /// ```
+    #[inline]
+    pub const fn try_new(value: u16) -> Result<VlanId, ValueTooBigError<u16>> {
+        use crate::err::ValueType;
+        if value <= VlanId::MAX_U16 {
+            Ok(VlanId(value))
+        } else {
+            Err(ValueTooBigError {
+                actual: value,
+                max_allowed: VlanId::MAX_U16,
+                value_type: ValueType::VlanId,
+            })
+        }
+    }
+
+    /// Creates an [`VlanId`] WITHOUT checking that the value
+    /// is smaller or equal than [`VlanId::MAX_U16`] (12 bit unsigned integer).
+    /// The caller must guarantee that `value <= VlanId::MAX_U16`.
+    ///
+    /// # Safety
+    ///
+    /// `value` must be smaller or equal than [`VlanId::MAX_U16`]
+    /// otherwise the behavior of functions or data structures relying
+    /// on this pre-requirement is undefined.
+    #[inline]
+    pub const unsafe fn new_unchecked(value: u16) -> VlanId {
+        debug_assert!(value <= VlanId::MAX_U16);
+        VlanId(value)
+    }
+
+    /// Returns the underlying unsigned 12 bit value as an `u16` value.
+    #[inline]
+    pub const fn value(self) -> u16 {
+        self.0
+    }
+}
+
+impl core::fmt::Display for VlanId {
+    #[inline]
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+impl From<VlanId> for u16 {
+    #[inline]
+    fn from(value: VlanId) -> Self {
+        value.0
+    }
+}
+
+impl TryFrom<u16> for VlanId {
+    type Error = ValueTooBigError<u16>;
+
+    #[inline]
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        use crate::err::ValueType;
+        if value <= VlanId::MAX_U16 {
+            Ok(VlanId(value))
+        } else {
+            Err(Self::Error {
+                actual: value,
+                max_allowed: VlanId::MAX_U16,
+                value_type: ValueType::VlanId,
+            })
+        }
+    }
+}
+
+#[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 = VlanId(2);
+            let b = a;
+            assert_eq!(a, b);
+            assert_eq!(a.clone(), a);
+        }
+
+        // default
+        {
+            let actual: VlanId = Default::default();
+            assert_eq!(actual.value(), 0);
+        }
+
+        // debug
+        {
+            let a = VlanId(2);
+            assert_eq!(format!("{:?}", a), format!("VlanId(2)"));
+        }
+
+        // ord & partial ord
+        {
+            use core::cmp::Ordering;
+            let a = VlanId(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();
+                VlanId(2).hash(&mut hasher);
+                hasher.finish()
+            };
+            let b = {
+                let mut hasher = DefaultHasher::new();
+                VlanId(2).hash(&mut hasher);
+                hasher.finish()
+            };
+            assert_eq!(a, b);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn try_new(
+            valid_value in 0..=0b0000_1111_1111_1111u16,
+            invalid_value in 0b0001_0000_0000_0000u16..=u16::MAX
+        ) {
+            use crate::err::{ValueType, ValueTooBigError};
+            assert_eq!(
+                valid_value,
+                VlanId::try_new(valid_value).unwrap().value()
+            );
+            assert_eq!(
+                VlanId::try_new(invalid_value).unwrap_err(),
+                ValueTooBigError{
+                    actual: invalid_value,
+                    max_allowed: 0b0000_1111_1111_1111,
+                    value_type:  ValueType::VlanId
+                }
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn try_from(
+            valid_value in 0..=0b0000_1111_1111_1111u16,
+            invalid_value in 0b0001_0000_0000_0000u16..=u16::MAX
+        ) {
+            use crate::err::{ValueType, ValueTooBigError};
+            // try_into
+            {
+                let actual: VlanId = valid_value.try_into().unwrap();
+                assert_eq!(actual.value(), valid_value);
+
+                let err: Result<VlanId, ValueTooBigError<u16>> = invalid_value.try_into();
+                assert_eq!(
+                    err.unwrap_err(),
+                    ValueTooBigError{
+                        actual: invalid_value,
+                        max_allowed: 0b0000_1111_1111_1111,
+                        value_type:  ValueType::VlanId
+                    }
+                );
+            }
+            // try_from
+            {
+                assert_eq!(
+                    VlanId::try_from(valid_value).unwrap().value(),
+                    valid_value
+                );
+
+                assert_eq!(
+                    VlanId::try_from(invalid_value).unwrap_err(),
+                    ValueTooBigError{
+                        actual: invalid_value,
+                        max_allowed: 0b0000_1111_1111_1111,
+                        value_type:  ValueType::VlanId
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn new_unchecked(valid_value in 0..=0b0000_1111_1111_1111u16) {
+            assert_eq!(
+                valid_value,
+                unsafe {
+                    VlanId::new_unchecked(valid_value).value()
+                }
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn fmt(valid_value in 0..=0b0000_1111_1111_1111u16) {
+            assert_eq!(format!("{}", VlanId(valid_value)), format!("{}", valid_value));
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from(valid_value in 0..=0b0000_1111_1111_1111u16,) {
+            let vlanid = VlanId::try_new(valid_value).unwrap();
+            let actual: u16 = vlanid.into();
+            assert_eq!(actual, valid_value);
+        }
+    }
+}
diff --git a/src/link/vlan_pcp.rs b/src/link/vlan_pcp.rs
new file mode 100644
index 0000000..7759bdb
--- /dev/null
+++ b/src/link/vlan_pcp.rs
@@ -0,0 +1,255 @@
+use crate::err::ValueTooBigError;
+
+/// 3 bit unsigned integer containing the "Priority Code Point"
+/// (present in the [`crate::SingleVlanHeader`]).
+///
+/// Refers to the IEEE 802.1p class of service and maps to the
+/// frame priority level.
+#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct VlanPcp(u8);
+
+impl VlanPcp {
+    /// VlanPcp with value 0.
+    pub const ZERO: VlanPcp = VlanPcp(0);
+
+    /// Maximum value of an vlan header PCP.
+    pub const MAX_U8: u8 = 0b0000_0111;
+
+    /// Tries to create an [`VlanPcp`] and checks that the passed value
+    /// is smaller or equal than [`VlanPcp::MAX_U8`] (3 bit unsigned integer).
+    ///
+    /// In case the passed value is bigger then what can be represented in an 3 bit
+    /// integer an error is returned. Otherwise an `Ok` containing the [`VlanPcp`].
+    ///
+    /// ```
+    /// use etherparse::VlanPcp;
+    ///
+    /// let pcp = VlanPcp::try_new(2).unwrap();
+    /// assert_eq!(pcp.value(), 2);
+    ///
+    /// // if a number that can not be represented in an 3 bit integer
+    /// // gets passed in an error is returned
+    /// use etherparse::err::{ValueTooBigError, ValueType};
+    /// assert_eq!(
+    ///     VlanPcp::try_new(VlanPcp::MAX_U8 + 1),
+    ///     Err(ValueTooBigError{
+    ///         actual: VlanPcp::MAX_U8 + 1,
+    ///         max_allowed: VlanPcp::MAX_U8,
+    ///         value_type: ValueType::VlanPcp,
+    ///     })
+    /// );
+    /// ```
+    #[inline]
+    pub const fn try_new(value: u8) -> Result<VlanPcp, ValueTooBigError<u8>> {
+        use crate::err::ValueType;
+        if value <= VlanPcp::MAX_U8 {
+            Ok(VlanPcp(value))
+        } else {
+            Err(ValueTooBigError {
+                actual: value,
+                max_allowed: VlanPcp::MAX_U8,
+                value_type: ValueType::VlanPcp,
+            })
+        }
+    }
+
+    /// Creates an [`VlanPcp`] without checking that the value
+    /// is smaller or equal than [`VlanPcp::MAX_U8`] (3 bit unsigned integer).
+    /// The caller must guarantee that `value <= VlanPcp::MAX_U8`.
+    ///
+    /// # Safety
+    ///
+    /// `value` must be smaller or equal than [`VlanPcp::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) -> VlanPcp {
+        debug_assert!(value <= VlanPcp::MAX_U8);
+        VlanPcp(value)
+    }
+
+    /// Returns the underlying unsigned 3 bit value as an `u8` value.
+    #[inline]
+    pub const fn value(self) -> u8 {
+        self.0
+    }
+}
+
+impl core::fmt::Display for VlanPcp {
+    #[inline]
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+impl From<VlanPcp> for u8 {
+    #[inline]
+    fn from(value: VlanPcp) -> Self {
+        value.0
+    }
+}
+
+impl TryFrom<u8> for VlanPcp {
+    type Error = ValueTooBigError<u8>;
+
+    #[inline]
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        use crate::err::ValueType;
+        if value <= VlanPcp::MAX_U8 {
+            Ok(VlanPcp(value))
+        } else {
+            Err(Self::Error {
+                actual: value,
+                max_allowed: VlanPcp::MAX_U8,
+                value_type: ValueType::VlanPcp,
+            })
+        }
+    }
+}
+
+#[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 = VlanPcp(2);
+            let b = a;
+            assert_eq!(a, b);
+            assert_eq!(a.clone(), a);
+        }
+
+        // default
+        {
+            let actual: VlanPcp = Default::default();
+            assert_eq!(actual.value(), 0);
+        }
+
+        // debug
+        {
+            let a = VlanPcp(2);
+            assert_eq!(format!("{:?}", a), format!("VlanPcp(2)"));
+        }
+
+        // ord & partial ord
+        {
+            use core::cmp::Ordering;
+            let a = VlanPcp(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();
+                VlanPcp(2).hash(&mut hasher);
+                hasher.finish()
+            };
+            let b = {
+                let mut hasher = DefaultHasher::new();
+                VlanPcp(2).hash(&mut hasher);
+                hasher.finish()
+            };
+            assert_eq!(a, b);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn try_new(
+            valid_value in 0..=0b0000_0111u8,
+            invalid_value in 0b0000_1000u8..=u8::MAX
+        ) {
+            use crate::err::{ValueType, ValueTooBigError};
+            assert_eq!(
+                valid_value,
+                VlanPcp::try_new(valid_value).unwrap().value()
+            );
+            assert_eq!(
+                VlanPcp::try_new(invalid_value).unwrap_err(),
+                ValueTooBigError{
+                    actual: invalid_value,
+                    max_allowed: 0b0000_0111,
+                    value_type:  ValueType::VlanPcp
+                }
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn try_from(
+            valid_value in 0..=0b0000_0111u8,
+            invalid_value in 0b0000_1000u8..=u8::MAX
+        ) {
+            use crate::err::{ValueType, ValueTooBigError};
+            // try_into
+            {
+                let actual: VlanPcp = valid_value.try_into().unwrap();
+                assert_eq!(actual.value(), valid_value);
+
+                let err: Result<VlanPcp, ValueTooBigError<u8>> = invalid_value.try_into();
+                assert_eq!(
+                    err.unwrap_err(),
+                    ValueTooBigError{
+                        actual: invalid_value,
+                        max_allowed: 0b0000_0111,
+                        value_type:  ValueType::VlanPcp
+                    }
+                );
+            }
+            // try_from
+            {
+                assert_eq!(
+                    VlanPcp::try_from(valid_value).unwrap().value(),
+                    valid_value
+                );
+
+                assert_eq!(
+                    VlanPcp::try_from(invalid_value).unwrap_err(),
+                    ValueTooBigError{
+                        actual: invalid_value,
+                        max_allowed: 0b0000_0111,
+                        value_type:  ValueType::VlanPcp
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn new_unchecked(valid_value in 0..=0b0000_0111u8) {
+            assert_eq!(
+                valid_value,
+                unsafe {
+                    VlanPcp::new_unchecked(valid_value).value()
+                }
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn fmt(valid_value in 0..=0b0000_0111u8) {
+            assert_eq!(format!("{}", VlanPcp(valid_value)), format!("{}", valid_value));
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from(valid_value in 0..=0b0000_0111u8,) {
+            let pcp = VlanPcp::try_new(valid_value).unwrap();
+            let actual: u8 = pcp.into();
+            assert_eq!(actual, valid_value);
+        }
+    }
+}
diff --git a/src/link/vlan_slice.rs b/src/link/vlan_slice.rs
new file mode 100644
index 0000000..8389085
--- /dev/null
+++ b/src/link/vlan_slice.rs
@@ -0,0 +1,122 @@
+use crate::*;
+
+/// A slice containing a single or double vlan header.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum VlanSlice<'a> {
+    SingleVlan(SingleVlanSlice<'a>),
+    DoubleVlan(DoubleVlanSlice<'a>),
+}
+
+impl<'a> VlanSlice<'a> {
+    /// Decode all the fields and copy the results to a VlanHeader struct
+    #[inline]
+    pub fn to_header(&self) -> VlanHeader {
+        use crate::VlanHeader::*;
+        use crate::VlanSlice::*;
+        match self {
+            SingleVlan(value) => Single(value.to_header()),
+            DoubleVlan(value) => Double(value.to_header()),
+        }
+    }
+
+    #[inline]
+    pub fn payload(&self) -> EtherPayloadSlice<'a> {
+        match self {
+            VlanSlice::SingleVlan(s) => s.payload(),
+            VlanSlice::DoubleVlan(d) => d.payload(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{test_gens::*, *};
+    use alloc::format;
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn to_header(
+            single in vlan_single_any(),
+            double in vlan_double_any(),
+        ) {
+            // single
+            {
+                let raw = single.to_bytes();
+                let slice = VlanSlice::SingleVlan(
+                    SingleVlanSlice::from_slice(&raw).unwrap()
+                );
+                assert_eq!(
+                    slice.to_header(),
+                    VlanHeader::Single(single)
+                );
+            }
+
+            // double
+            {
+                let raw = double.to_bytes();
+                let slice = VlanSlice::DoubleVlan(
+                    DoubleVlanSlice::from_slice(&raw).unwrap()
+                );
+                assert_eq!(
+                    slice.to_header(),
+                    VlanHeader::Double(double)
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn debug(
+            single in vlan_single_any(),
+            double in vlan_double_any(),
+        ) {
+            // single
+            {
+                let raw = single.to_bytes();
+                let s = SingleVlanSlice::from_slice(&raw).unwrap();
+                assert_eq!(
+                    format!("{:?}", VlanSlice::SingleVlan(s.clone())),
+                    format!("SingleVlan({:?})", s)
+                );
+            }
+
+            // double
+            {
+                let raw = double.to_bytes();
+                let d = DoubleVlanSlice::from_slice(&raw).unwrap();
+                assert_eq!(
+                    format!("{:?}", VlanSlice::DoubleVlan(d.clone())),
+                    format!("DoubleVlan({:?})", d)
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(
+            single in vlan_single_any(),
+            double in vlan_double_any(),
+        ) {
+            // single
+            {
+                let raw = single.to_bytes();
+                let s = VlanSlice::SingleVlan(
+                    SingleVlanSlice::from_slice(&raw).unwrap()
+                );
+                assert_eq!(s.clone(), s);
+            }
+
+            // double
+            {
+                let raw = double.to_bytes();
+                let d = VlanSlice::DoubleVlan(
+                    DoubleVlanSlice::from_slice(&raw).unwrap()
+                );
+                assert_eq!(d.clone(), d);
+            }
+        }
+    }
+}
diff --git a/src/net/ip_auth_header.rs b/src/net/ip_auth_header.rs
new file mode 100644
index 0000000..2d406a1
--- /dev/null
+++ b/src/net/ip_auth_header.rs
@@ -0,0 +1,682 @@
+use super::super::*;
+use crate::err::ip_auth::IcvLenError;
+use arrayvec::ArrayVec;
+use core::fmt::{Debug, Formatter};
+
+/// Deprecated use [IpAuthHeader] instead.
+#[deprecated(since = "0.10.1", note = "Please use the type IpAuthHeader instead")]
+pub type IPv6AuthenticationHeader = IpAuthHeader;
+
+/// Deprecated use [IpAuthHeader] instead.
+#[deprecated(since = "0.14.0", note = "Please use the type IpAuthHeader instead")]
+pub type IpAuthenticationHeader = IpAuthHeader;
+
+/// IP Authentication Header (rfc4302)
+#[derive(Clone)]
+pub struct IpAuthHeader {
+    /// IP protocol number specifying the next header or transport layer protocol.
+    ///
+    /// See [IpNumber] or [ip_number] for a definition of the known values.
+    pub next_header: IpNumber,
+    /// Security Parameters Index
+    pub spi: u32,
+    /// This unsigned 32-bit field contains a counter value that
+    /// increases by one for each packet sent.
+    pub sequence_number: u32,
+    /// Length in 4-octets (maximum valid value is 0xfe) of data filled in the
+    /// `raw_icv_buffer`.
+    raw_icv_len: u8,
+    /// Buffer containing the "Encoded Integrity Check Value-ICV" (variable).
+    /// The length of the used data can be set via the `variable` (must be a multiple of 4 bytes).
+    raw_icv_buffer: [u8; 0xfe * 4],
+}
+
+impl Debug for IpAuthHeader {
+    fn fmt(&self, formatter: &mut Formatter) -> Result<(), core::fmt::Error> {
+        let mut s = formatter.debug_struct("IpAuthHeader");
+        s.field("next_header", &self.next_header);
+        s.field("spi", &self.spi);
+        s.field("sequence_number", &self.sequence_number);
+        s.field("raw_icv", &self.raw_icv());
+        s.finish()
+    }
+}
+
+impl PartialEq for IpAuthHeader {
+    fn eq(&self, other: &Self) -> bool {
+        self.next_header == other.next_header
+            && self.spi == other.spi
+            && self.sequence_number == other.sequence_number
+            && self.raw_icv() == other.raw_icv()
+    }
+}
+
+impl Eq for IpAuthHeader {}
+
+impl Default for IpAuthHeader {
+    fn default() -> Self {
+        IpAuthHeader {
+            next_header: IpNumber(255),
+            spi: 0,
+            sequence_number: 0,
+            raw_icv_len: 0,
+            raw_icv_buffer: [0; 0xfe * 4],
+        }
+    }
+}
+
+impl<'a> IpAuthHeader {
+    /// Minimum length of an IP authentication header in bytes/octets.
+    pub const MIN_LEN: usize = 4 + 4 + 4;
+
+    /// Maximum length of an IP authentication header in bytes/octets.
+    ///
+    /// This number is calculated by taking the maximum value
+    /// that the "payload length" field supports (0xff) adding 2 and
+    /// multiplying the sum by 4 as the "payload length" specifies how
+    /// many 4 bytes words are present in the header.
+    pub const MAX_LEN: usize = 4 * (0xff + 2);
+
+    /// The maximum amount of bytes/octets that can be stored in the ICV
+    /// part of an IP authentication header.
+    pub const MAX_ICV_LEN: usize = 0xfe * 4;
+
+    /// Create a new authentication header with the given parameters.
+    ///
+    /// Note: The length of the raw_icv slice must be a multiple of 4
+    /// and the maximum allowed length is 1016 bytes
+    /// (`IpAuthHeader::MAX_ICV_LEN`). If the slice length does
+    /// not fulfill these requirements the value is not copied and an
+    /// [`crate::err::ip_auth::IcvLenError`] is returned.
+    /// If successful an Ok(()) is returned.
+    pub fn new(
+        next_header: IpNumber,
+        spi: u32,
+        sequence_number: u32,
+        raw_icv: &'a [u8],
+    ) -> Result<IpAuthHeader, IcvLenError> {
+        use IcvLenError::*;
+        if raw_icv.len() > IpAuthHeader::MAX_ICV_LEN {
+            Err(TooBig(raw_icv.len()))
+        } else if 0 != raw_icv.len() % 4 {
+            Err(Unaligned(raw_icv.len()))
+        } else {
+            let mut result = IpAuthHeader {
+                next_header,
+                spi,
+                sequence_number,
+                raw_icv_len: (raw_icv.len() / 4) as u8,
+                raw_icv_buffer: [0; IpAuthHeader::MAX_ICV_LEN],
+            };
+            result.raw_icv_buffer[..raw_icv.len()].copy_from_slice(raw_icv);
+            Ok(result)
+        }
+    }
+
+    /// Read an  authentication header from a slice and return the header & unused parts of the slice.
+    pub fn from_slice(
+        slice: &'a [u8],
+    ) -> Result<(IpAuthHeader, &'a [u8]), err::ip_auth::HeaderSliceError> {
+        let s = IpAuthHeaderSlice::from_slice(slice)?;
+        let rest = &slice[s.slice().len()..];
+        let header = s.to_header();
+        Ok((header, rest))
+    }
+
+    /// Read an authentication header from the current reader position.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read<T: std::io::Read + Sized>(
+        reader: &mut T,
+    ) -> Result<IpAuthHeader, err::ip_auth::HeaderReadError> {
+        use err::ip_auth::HeaderError::*;
+        use err::ip_auth::HeaderReadError::*;
+
+        let start = {
+            let mut start = [0; 4 + 4 + 4];
+            reader.read_exact(&mut start).map_err(Io)?;
+            start
+        };
+
+        let next_header = IpNumber(start[0]);
+        let payload_len = start[1];
+
+        // payload len must be at least 1
+        if payload_len < 1 {
+            Err(Content(ZeroPayloadLen))
+        } else {
+            // read the rest of the header
+            Ok(IpAuthHeader {
+                next_header,
+                spi: u32::from_be_bytes([start[4], start[5], start[6], start[7]]),
+                sequence_number: u32::from_be_bytes([start[8], start[9], start[10], start[11]]),
+                raw_icv_len: payload_len - 1,
+                raw_icv_buffer: {
+                    let mut buffer = [0; 0xfe * 4];
+                    reader
+                        .read_exact(&mut buffer[..usize::from(payload_len - 1) * 4])
+                        .map_err(Io)?;
+                    buffer
+                },
+            })
+        }
+    }
+
+    /// Read an authentication header from the current reader position
+    /// with a limited reader.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read_limited<T: std::io::Read + Sized>(
+        reader: &mut crate::io::LimitedReader<T>,
+    ) -> Result<IpAuthHeader, err::ip_auth::HeaderLimitedReadError> {
+        use err::{
+            ip_auth::HeaderError::*,
+            ip_auth::HeaderLimitedReadError::{self, *},
+            Layer,
+        };
+
+        fn map_err(err: err::io::LimitedReadError) -> HeaderLimitedReadError {
+            use err::io::LimitedReadError as I;
+            match err {
+                I::Io(err) => Io(err),
+                I::Len(err) => Len(err),
+            }
+        }
+
+        // notify reader of layer start
+        reader.start_layer(Layer::IpAuthHeader);
+
+        let start = {
+            let mut start = [0; 4 + 4 + 4];
+            reader.read_exact(&mut start).map_err(map_err)?;
+            start
+        };
+
+        let next_header = IpNumber(start[0]);
+        let payload_len = start[1];
+
+        // payload len must be at least 1
+        if payload_len < 1 {
+            Err(Content(ZeroPayloadLen))
+        } else {
+            // read the rest of the header
+            Ok(IpAuthHeader {
+                next_header,
+                spi: u32::from_be_bytes([start[4], start[5], start[6], start[7]]),
+                sequence_number: u32::from_be_bytes([start[8], start[9], start[10], start[11]]),
+                raw_icv_len: payload_len - 1,
+                raw_icv_buffer: {
+                    let mut buffer = [0; 0xfe * 4];
+                    reader
+                        .read_exact(&mut buffer[..usize::from(payload_len - 1) * 4])
+                        .map_err(map_err)?;
+                    buffer
+                },
+            })
+        }
+    }
+
+    /// Returns a slice the raw icv value.
+    pub fn raw_icv(&self) -> &[u8] {
+        &self.raw_icv_buffer[..usize::from(self.raw_icv_len) * 4]
+    }
+
+    /// Sets the icv value to the given raw value. The length of the slice must be
+    /// a multiple of 4 and the maximum allowed length is 1016 bytes
+    /// (`IpAuthHeader::MAX_ICV_LEN`). If the slice length does
+    /// not fulfill these requirements the value is not copied and an
+    /// [`crate::err::ip_auth::IcvLenError`] is returned.
+    /// If successful an Ok(()) is returned.
+    pub fn set_raw_icv(&mut self, raw_icv: &[u8]) -> Result<(), IcvLenError> {
+        use IcvLenError::*;
+        if raw_icv.len() > IpAuthHeader::MAX_ICV_LEN {
+            Err(TooBig(raw_icv.len()))
+        } else if 0 != raw_icv.len() % 4 {
+            Err(Unaligned(raw_icv.len()))
+        } else {
+            self.raw_icv_buffer[..raw_icv.len()].copy_from_slice(raw_icv);
+            self.raw_icv_len = (raw_icv.len() / 4) as u8;
+            Ok(())
+        }
+    }
+
+    /// Writes the given authentication header to the current position.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
+        let spi_be = self.spi.to_be_bytes();
+        let sequence_number_be = self.sequence_number.to_be_bytes();
+        debug_assert!(self.raw_icv_len != 0xff);
+
+        writer.write_all(&[
+            self.next_header.0,
+            self.raw_icv_len + 1,
+            0,
+            0,
+            spi_be[0],
+            spi_be[1],
+            spi_be[2],
+            spi_be[3],
+            sequence_number_be[0],
+            sequence_number_be[1],
+            sequence_number_be[2],
+            sequence_number_be[3],
+        ])?;
+        writer.write_all(self.raw_icv())?;
+        Ok(())
+    }
+
+    ///Length of the header in bytes.
+    pub fn header_len(&self) -> usize {
+        12 + usize::from(self.raw_icv_len) * 4
+    }
+
+    /// Returns the serialized header.
+    pub fn to_bytes(&self) -> ArrayVec<u8, { IpAuthHeader::MAX_LEN }> {
+        let spi_be = self.spi.to_be_bytes();
+        let seq_be = self.sequence_number.to_be_bytes();
+
+        let mut result = ArrayVec::<u8, { IpAuthHeader::MAX_LEN }>::new();
+        result.extend([
+            self.next_header.0,
+            self.raw_icv_len + 1,
+            0,
+            0,
+            spi_be[0],
+            spi_be[1],
+            spi_be[2],
+            spi_be[3],
+            seq_be[0],
+            seq_be[1],
+            seq_be[2],
+            seq_be[3],
+        ]);
+        result.extend(self.raw_icv_buffer);
+        // SAFETY: Safe as the header len can not exceed the maximum length
+        // of the header.
+        unsafe {
+            result.set_len(self.header_len());
+        }
+
+        result
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::{
+        err::{Layer, LenError},
+        io::LimitedReader,
+        test_gens::*,
+    };
+    use alloc::{format, vec::Vec};
+    use err::ip_auth::HeaderError::*;
+    use proptest::prelude::*;
+    use std::io::Cursor;
+
+    #[test]
+    fn default() {
+        let default_header = IpAuthHeader {
+            ..Default::default()
+        };
+
+        assert_eq!(default_header.next_header, IpNumber(255));
+        assert_eq!(default_header.spi, 0);
+        assert_eq!(default_header.sequence_number, 0);
+        assert_eq!(default_header.raw_icv_len, 0);
+        assert_eq!(default_header.raw_icv_buffer, [0; 0xfe * 4]);
+    }
+
+    proptest! {
+        #[test]
+        fn debug(input in ip_auth_any()) {
+            assert_eq!(
+                &format!(
+                    "IpAuthHeader {{ next_header: {:?}, spi: {}, sequence_number: {}, raw_icv: {:?} }}",
+                    input.next_header,
+                    input.spi,
+                    input.sequence_number,
+                    input.raw_icv()),
+                &format!("{:?}", input)
+            );
+        }
+    }
+
+    #[test]
+    pub fn clone() {
+        let a = IpAuthHeader::new(0.into(), 0, 0, &[0; 4]);
+        assert_eq!(a.clone(), a);
+    }
+
+    #[test]
+    pub fn partial_eq() {
+        let a = IpAuthHeader::new(0.into(), 0, 0, &[0; 4]);
+
+        //equal
+        assert!(a == IpAuthHeader::new(0.into(), 0, 0, &[0; 4]));
+
+        //not equal tests
+        assert!(a != IpAuthHeader::new(1.into(), 0, 0, &[0; 4]));
+        assert!(a != IpAuthHeader::new(0.into(), 1, 0, &[0; 4]));
+        assert!(a != IpAuthHeader::new(0.into(), 0, 1, &[0; 4]));
+        assert!(a != IpAuthHeader::new(0.into(), 0, 0, &[0, 1, 0, 0]));
+        assert!(a != IpAuthHeader::new(0.into(), 0, 1, &[]));
+        assert!(a != IpAuthHeader::new(0.into(), 0, 1, &[0; 8]));
+    }
+
+    #[test]
+    fn new_and_set_icv() {
+        use IcvLenError::*;
+
+        struct Test {
+            icv: &'static [u8],
+            err: Option<IcvLenError>,
+        }
+
+        let tests = [
+            // ok
+            Test {
+                icv: &[],
+                err: None,
+            },
+            Test {
+                icv: &[1, 2, 3, 4],
+                err: None,
+            },
+            Test {
+                icv: &[1, 2, 3, 4, 5, 6, 7, 8],
+                err: None,
+            },
+            Test {
+                icv: &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
+                err: None,
+            },
+            Test {
+                icv: &[0; 0xfe * 4],
+                err: None,
+            },
+            // unaligned
+            Test {
+                icv: &[1],
+                err: Some(Unaligned(1)),
+            },
+            Test {
+                icv: &[1, 2, 3],
+                err: Some(Unaligned(3)),
+            },
+            Test {
+                icv: &[1, 2, 3, 4, 5],
+                err: Some(Unaligned(5)),
+            },
+            Test {
+                icv: &[1, 2, 3, 4, 5, 6, 7],
+                err: Some(Unaligned(7)),
+            },
+            // too big
+            Test {
+                icv: &[0; 0xff * 4],
+                err: Some(TooBig(0xff * 4)),
+            },
+        ];
+
+        for test in tests.iter() {
+            // new
+            {
+                let a = IpAuthHeader::new(5.into(), 6, 7, test.icv);
+                if let Some(err) = &test.err {
+                    assert_eq!(Err(err.clone()), a);
+                } else {
+                    let unwrapped = a.unwrap();
+                    assert_eq!(IpNumber(5), unwrapped.next_header);
+                    assert_eq!(6, unwrapped.spi);
+                    assert_eq!(7, unwrapped.sequence_number);
+                    assert_eq!(test.icv, unwrapped.raw_icv());
+                }
+            }
+            // set_raw_icv
+            {
+                let mut header = IpAuthHeader::new(5.into(), 6, 7, &[0; 4]).unwrap();
+                let result = header.set_raw_icv(test.icv);
+                assert_eq!(IpNumber(5), header.next_header);
+                assert_eq!(6, header.spi);
+                assert_eq!(7, header.sequence_number);
+                if let Some(err) = &test.err {
+                    assert_eq!(Err(err.clone()), result);
+                    assert_eq!(&[0; 4], header.raw_icv());
+                } else {
+                    assert_eq!(Ok(()), result);
+                    assert_eq!(test.icv, header.raw_icv());
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(header in ip_auth_any()) {
+            use err::ip_auth::HeaderSliceError::*;
+
+            // ok
+            {
+                let mut bytes = ArrayVec::<u8, {IpAuthHeader::MAX_LEN + 2}>::new();
+                bytes.extend(header.to_bytes());
+                bytes.push(1);
+                bytes.push(2);
+
+                let (actual_header, actual_slice) = IpAuthHeader::from_slice(&bytes).unwrap();
+                assert_eq!(header, actual_header);
+                assert_eq!(&[1,2], actual_slice);
+            }
+
+            // length error
+            {
+                let bytes = header.to_bytes();
+                for len in 0..header.header_len() {
+                    assert_eq!(
+                        IpAuthHeader::from_slice(&bytes[..len]).unwrap_err(),
+                        Len(err::LenError{
+                            required_len: if len < IpAuthHeader::MIN_LEN {
+                                IpAuthHeader::MIN_LEN
+                            } else {
+                                header.header_len()
+                            },
+                            len: len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::IpAuthHeader,
+                            layer_start_offset: 0,
+                        })
+                    );
+                }
+            }
+
+            // payload length error
+            {
+                let mut bytes = header.to_bytes();
+                // set payload length to 0
+                bytes[1] = 0;
+                assert_eq!(
+                    IpAuthHeader::from_slice(&bytes).unwrap_err(),
+                    Content(ZeroPayloadLen)
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read(header in ip_auth_any()) {
+            // ok
+            {
+                let bytes = header.to_bytes();
+                let mut cursor = Cursor::new(&bytes);
+                assert_eq!(header, IpAuthHeader::read(&mut cursor).unwrap());
+            }
+
+            // length error
+            {
+                let bytes = header.to_bytes();
+                for len in 0..header.header_len() {
+                    let mut cursor = Cursor::new(&bytes[..len]);
+                    assert!(
+                        IpAuthHeader::read(&mut cursor)
+                            .unwrap_err()
+                            .io()
+                            .is_some()
+                    );
+                }
+            }
+
+            // payload length error
+            {
+                let mut bytes = header.to_bytes();
+                // set payload length to 0
+                bytes[1] = 0;
+                let mut cursor = Cursor::new(&bytes);
+                assert_eq!(
+                    IpAuthHeader::read(&mut cursor).unwrap_err().content(),
+                    Some(ZeroPayloadLen)
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read_limited(header in ip_auth_any()) {
+            // ok
+            {
+                let bytes = header.to_bytes();
+                let mut cursor = Cursor::new(&bytes);
+                let mut reader = LimitedReader::new(
+                    &mut cursor,
+                    bytes.len(),
+                    LenSource::Slice,
+                    0,
+                    Layer::Ipv4Header
+                );
+                assert_eq!(header, IpAuthHeader::read_limited(&mut reader).unwrap());
+            }
+
+            // length error
+            {
+                let bytes = header.to_bytes();
+                for len in 0..header.header_len() {
+                    // io error
+                    {
+                        let mut cursor = Cursor::new(&bytes[..len]);
+                        let mut reader = LimitedReader::new(
+                            &mut cursor,
+                            bytes.len(),
+                            LenSource::Slice,
+                            0,
+                            Layer::Ipv4Header
+                        );
+                        assert!(
+                            IpAuthHeader::read_limited(&mut reader)
+                                .unwrap_err()
+                                .io()
+                                .is_some()
+                        );
+                    }
+                    // limited reader error
+                    {
+
+                        let mut cursor = Cursor::new(&bytes);
+                        let mut reader = LimitedReader::new(
+                            &mut cursor,
+                            len,
+                            LenSource::Ipv4HeaderTotalLen,
+                            0,
+                            Layer::Ipv4Header
+                        );
+                        assert_eq!(
+                            IpAuthHeader::read_limited(&mut reader)
+                                .unwrap_err()
+                                .len()
+                                .unwrap(),
+                            LenError {
+                                required_len: if len < 12 {
+                                    12
+                                } else {
+                                    bytes.len()
+                                },
+                                len,
+                                len_source: LenSource::Ipv4HeaderTotalLen,
+                                layer: Layer::IpAuthHeader,
+                                layer_start_offset: 0
+                            }
+                        );
+                    }
+                }
+            }
+
+            // payload length error
+            {
+                let mut bytes = header.to_bytes();
+                // set payload length to 0
+                bytes[1] = 0;
+                let mut cursor = Cursor::new(&bytes);
+                let mut reader = LimitedReader::new(
+                    &mut cursor,
+                    bytes.len(),
+                    LenSource::Ipv4HeaderTotalLen,
+                    0,
+                    Layer::Ipv4Header
+                );
+                assert_eq!(
+                    IpAuthHeader::read_limited(&mut reader).unwrap_err().content(),
+                    Some(ZeroPayloadLen)
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write(header in ip_auth_any()) {
+
+            // ok case
+            {
+                let mut buffer: Vec<u8> = Vec::with_capacity(header.header_len());
+                header.write(&mut buffer).unwrap();
+                assert_eq!(header, IpAuthHeader::from_slice(&buffer).unwrap().0);
+            };
+
+            // io error
+            for len in 0..header.header_len() {
+                let mut buffer = [0u8;IpAuthHeader::MAX_LEN];
+                let mut cursor = Cursor::new(&mut buffer[..len]);
+                assert!(header.write(&mut cursor).is_err());
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header_len(header in ip_auth_any()) {
+            assert_eq!(header.header_len(), header.raw_icv().len() + 12);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_bytes(header in ip_auth_any()) {
+            let bytes = header.to_bytes();
+
+            assert_eq!(header.next_header.0, bytes[0]);
+            assert_eq!((header.header_len()/4 - 2) as u8, bytes[1]);
+            assert_eq!(0, bytes[2]);
+            assert_eq!(0, bytes[3]);
+            {
+                let spi = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
+                assert_eq!(spi, header.spi);
+            }
+            {
+                let seq_nr = u32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]);
+                assert_eq!(seq_nr, header.sequence_number);
+            }
+            assert_eq!(&bytes[12..], header.raw_icv());
+        }
+    }
+}
diff --git a/src/net/ip_auth_header_slice.rs b/src/net/ip_auth_header_slice.rs
new file mode 100644
index 0000000..9c787c1
--- /dev/null
+++ b/src/net/ip_auth_header_slice.rs
@@ -0,0 +1,257 @@
+use crate::*;
+use core::slice::from_raw_parts;
+
+/// Deprecated use [IpAuthHeaderSlice] instead.
+#[deprecated(
+    since = "0.14.0",
+    note = "Please use the type IpAuthHeaderSlice instead"
+)]
+pub type IpAuthenticationHeaderSlice<'a> = IpAuthHeaderSlice<'a>;
+
+/// A slice containing an IP Authentication Header (rfc4302)
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct IpAuthHeaderSlice<'a> {
+    slice: &'a [u8],
+}
+
+impl<'a> IpAuthHeaderSlice<'a> {
+    /// Creates a ip authentication header slice from a slice.
+    pub fn from_slice(
+        slice: &'a [u8],
+    ) -> Result<IpAuthHeaderSlice<'a>, err::ip_auth::HeaderSliceError> {
+        use err::ip_auth::{HeaderError::*, HeaderSliceError::*};
+
+        // check slice length
+        if slice.len() < IpAuthHeader::MIN_LEN {
+            return Err(Len(err::LenError {
+                required_len: IpAuthHeader::MIN_LEN,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::IpAuthHeader,
+                layer_start_offset: 0,
+            }));
+        }
+
+        // SAFETY:
+        // Safe the slice length gets checked to be at least 12 beforehand.
+        let payload_len_enc = unsafe { *slice.get_unchecked(1) };
+
+        // check header length minimum size
+        if payload_len_enc < 1 {
+            return Err(Content(ZeroPayloadLen));
+        }
+
+        // check length
+        // note: The unit is different then all other ipv6 extension headers.
+        //       In the other headers the length is in 8 octets, but for authentication
+        //       headers the length is in 4 octets.
+        let len = ((payload_len_enc as usize) + 2) * 4;
+        if slice.len() < len {
+            return Err(Len(err::LenError {
+                required_len: len,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::IpAuthHeader,
+                layer_start_offset: 0,
+            }));
+        }
+
+        // all good
+        Ok(IpAuthHeaderSlice {
+            // SAFETY:
+            // Safe as slice len is checked to be at last len above.
+            slice: unsafe { from_raw_parts(slice.as_ptr(), len) },
+        })
+    }
+
+    /// Creates a ip authentication header slice from a slice (assumes slice size & content was validated before).
+    ///
+    /// # Safety
+    ///
+    /// This method assumes that the slice was previously validated to contain
+    /// a valid authentication header. This means the slice length must at
+    /// least be at least 8 and `(slice[1] + 2)*4`. The data that the
+    /// slice points must also be valid (meaning no nullptr or alike allowed).
+    ///
+    /// If these preconditions are not fulfilled the behavior of this function
+    /// and the methods of the return IpAuthHeaderSlice will be undefined.
+    pub unsafe fn from_slice_unchecked(slice: &'a [u8]) -> IpAuthHeaderSlice<'a> {
+        IpAuthHeaderSlice {
+            slice: from_raw_parts(slice.as_ptr(), ((*slice.get_unchecked(1) as usize) + 2) * 4),
+        }
+    }
+
+    /// Returns the slice containing the authentication header.
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+
+    /// Returns the IP protocol number of the next header or transport layer protocol.
+    ///
+    /// See [IpNumber] or [ip_number] for a definition of the known values.
+    #[inline]
+    pub fn next_header(&self) -> IpNumber {
+        // SAFETY:
+        // Safe as slice length is checked in the constructor
+        // to be at least 12.
+        IpNumber(unsafe { *self.slice.get_unchecked(0) })
+    }
+
+    /// Read the security parameters index from the slice
+    #[inline]
+    pub fn spi(&self) -> u32 {
+        // SAFETY:
+        // Safe as slice length is checked in the constructor
+        // to be at least 12.
+        unsafe { get_unchecked_be_u32(self.slice.as_ptr().add(4)) }
+    }
+
+    /// This unsigned 32-bit field contains a counter value that
+    /// increases by one for each packet sent.
+    #[inline]
+    pub fn sequence_number(&self) -> u32 {
+        // SAFETY:
+        // Safe as slice length is checked in the constructor
+        // to be at least 12.
+        unsafe { get_unchecked_be_u32(self.slice.as_ptr().add(8)) }
+    }
+
+    /// Return a slice with the raw integrity check value
+    pub fn raw_icv(&self) -> &'a [u8] {
+        &self.slice[12..]
+    }
+
+    /// Decode some of the fields and copy the results to a
+    /// Ipv6ExtensionHeader struct together with a slice pointing
+    /// to the non decoded parts.
+    pub fn to_header(&self) -> IpAuthHeader {
+        IpAuthHeader::new(
+            self.next_header(),
+            self.spi(),
+            self.sequence_number(),
+            self.raw_icv(),
+        )
+        .unwrap()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::format;
+    use arrayvec::ArrayVec;
+    use err::ip_auth::{HeaderError::*, HeaderSliceError::*};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn debug(input in ip_auth_any()) {
+            let buffer = input.to_bytes();
+            let slice = IpAuthHeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(
+                &format!(
+                    "IpAuthHeaderSlice {{ slice: {:?} }}",
+                    slice.slice()
+                ),
+                &format!("{:?}", slice)
+            );
+        }
+    }
+
+    #[test]
+    fn clone_eq() {
+        let buffer = IpAuthHeader::new(0.into(), 0, 0, &[0; 4])
+            .unwrap()
+            .to_bytes();
+        let slice = IpAuthHeaderSlice::from_slice(&buffer).unwrap();
+        assert_eq!(slice.clone(), slice);
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(header in ip_auth_any()) {
+
+            // ok
+            {
+                let mut bytes = ArrayVec::<u8, {IpAuthHeader::MAX_LEN + 2}>::new();
+                bytes.extend(header.to_bytes());
+                bytes.push(1);
+                bytes.push(2);
+
+                let slice = IpAuthHeaderSlice::from_slice(&bytes).unwrap();
+                assert_eq!(slice.slice(), &bytes[..bytes.len() - 2]);
+            }
+
+            // length error
+            {
+                let bytes = header.to_bytes();
+                for len in 0..header.header_len() {
+                    assert_eq!(
+                        IpAuthHeaderSlice::from_slice(&bytes[..len]).unwrap_err(),
+                        Len(err::LenError{
+                            required_len: if len < IpAuthHeader::MIN_LEN {
+                                IpAuthHeader::MIN_LEN
+                            } else {
+                                header.header_len()
+                            },
+                            len: len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::IpAuthHeader,
+                            layer_start_offset: 0,
+                        })
+                    );
+                }
+            }
+
+            // payload length error
+            {
+                let mut bytes = header.to_bytes();
+                // set payload length to 0
+                bytes[1] = 0;
+                assert_eq!(
+                    IpAuthHeaderSlice::from_slice(&bytes).unwrap_err(),
+                    Content(ZeroPayloadLen)
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice_unchecked(header in ip_auth_any()) {
+            let bytes = header.to_bytes();
+            let slice = unsafe {
+                IpAuthHeaderSlice::from_slice_unchecked(&bytes)
+            };
+            assert_eq!(slice.slice(), &bytes[..]);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn getters(header in ip_auth_any()) {
+            let bytes = header.to_bytes();
+            let slice = IpAuthHeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(slice.slice(), &bytes[..]);
+            assert_eq!(slice.next_header(), header.next_header);
+            assert_eq!(slice.spi(), header.spi);
+            assert_eq!(slice.sequence_number(), header.sequence_number);
+            assert_eq!(slice.raw_icv(), header.raw_icv());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_header(header in ip_auth_any()) {
+            let bytes = header.to_bytes();
+            assert_eq!(
+                header,
+                IpAuthHeaderSlice::from_slice(&bytes)
+                    .unwrap()
+                    .to_header()
+            );
+        }
+    }
+}
diff --git a/src/net/ip_frag_offset.rs b/src/net/ip_frag_offset.rs
new file mode 100644
index 0000000..8370baf
--- /dev/null
+++ b/src/net/ip_frag_offset.rs
@@ -0,0 +1,308 @@
+use crate::err::ValueTooBigError;
+
+/// The fragment offset is a 13 bit unsigned integer indicating the stating
+/// position of the payload of a packet relative to the originally fragmented
+/// packet payload.
+///
+/// This value can be present in an [`crate::Ipv4Header`] or an
+/// [`crate::Ipv6FragmentHeader`].
+///
+/// # Example Usage:
+///
+/// ```
+/// use etherparse::IpFragOffset;
+///
+/// // try into
+/// {
+///     let frag_offset: IpFragOffset = 123.try_into().unwrap();
+///     assert_eq!(frag_offset.value(), 123);
+///
+///     // fragment offset can always be converted back to an u16
+///     let value: u16 = frag_offset.into();
+///     assert_eq!(123, value);
+/// }
+///
+/// // via try_new
+/// {
+///     let frag_offset = IpFragOffset::try_new(123).unwrap();
+///     assert_eq!(frag_offset.value(), 123);
+///
+///     // note that only 13 bit numbers are allowed (meaning
+///     // 0b0001_1111_1111_1111 is the maximum allowed value)
+///     use etherparse::err::{ValueTooBigError, ValueType};
+///     assert_eq!(
+///         IpFragOffset::try_new(IpFragOffset::MAX_U16 + 1),
+///         Err(ValueTooBigError{
+///             actual: IpFragOffset::MAX_U16 + 1,
+///             max_allowed: IpFragOffset::MAX_U16,
+///             value_type: ValueType::IpFragmentOffset,
+///         })
+///     );
+/// }
+///
+/// // via new_unchecked
+/// {
+///     // in case you are sure the number does not exceed the max
+///     // you can use the unsafe new_unchecked function
+///     let frag_offset = unsafe {
+///         // please make sure that the value is not greater than IpFragOffset::MAX_U16
+///         // before calling this method
+///         IpFragOffset::new_unchecked(123)
+///     };
+///     assert_eq!(frag_offset.value(), 123);
+/// }
+/// ```
+#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct IpFragOffset(u16);
+
+impl IpFragOffset {
+    /// IpFragOffset with value 0.
+    pub const ZERO: IpFragOffset = IpFragOffset(0);
+
+    /// Maximum value of an IP fragmentation offset.
+    pub const MAX_U16: u16 = 0b0001_1111_1111_1111;
+
+    /// Tries to create an [`IpFragOffset`] and checks that the passed value
+    /// is smaller or equal than [`IpFragOffset::MAX_U16`] (13 bit unsigned integer).
+    ///
+    /// In case the passed value is bigger then what can be represented in an 13 bit
+    /// integer an error is returned. Otherwise an `Ok` containing the [`IpFragOffset`].
+    ///
+    /// ```
+    /// use etherparse::IpFragOffset;
+    ///
+    /// let frag_offset = IpFragOffset::try_new(123).unwrap();
+    /// assert_eq!(frag_offset.value(), 123);
+    ///
+    /// // if a number that can not be represented in an 13 bit integer
+    /// // gets passed in an error is returned
+    /// use etherparse::err::{ValueTooBigError, ValueType};
+    /// assert_eq!(
+    ///     IpFragOffset::try_new(IpFragOffset::MAX_U16 + 1),
+    ///     Err(ValueTooBigError{
+    ///         actual: IpFragOffset::MAX_U16 + 1,
+    ///         max_allowed: IpFragOffset::MAX_U16,
+    ///         value_type: ValueType::IpFragmentOffset,
+    ///     })
+    /// );
+    /// ```
+    #[inline]
+    pub const fn try_new(value: u16) -> Result<IpFragOffset, ValueTooBigError<u16>> {
+        use crate::err::ValueType::IpFragmentOffset;
+        if value <= IpFragOffset::MAX_U16 {
+            Ok(IpFragOffset(value))
+        } else {
+            Err(ValueTooBigError {
+                actual: value,
+                max_allowed: IpFragOffset::MAX_U16,
+                value_type: IpFragmentOffset,
+            })
+        }
+    }
+
+    /// Creates an [`IpFragOffset`] without checking that the value
+    /// is smaller or equal than [`IpFragOffset::MAX_U16`] (13 bit unsigned integer).
+    /// The caller must guarantee that `value <= IpFragOffset::MAX_U16`.
+    ///
+    /// # Safety
+    ///
+    /// `value` must be smaller or equal than [`IpFragOffset::MAX_U16`]
+    /// otherwise the behavior of functions or data structures relying
+    /// on this pre-requirement is undefined.
+    #[inline]
+    pub const unsafe fn new_unchecked(value: u16) -> IpFragOffset {
+        debug_assert!(value <= IpFragOffset::MAX_U16);
+        IpFragOffset(value)
+    }
+
+    /// Returns the underlying unsigned 13 bit value as an `u16` value.
+    #[inline]
+    pub const fn value(self) -> u16 {
+        self.0
+    }
+
+    /// Returns the offset in bytes (offset raw value multiplied by 8).
+    #[inline]
+    pub const fn byte_offset(self) -> u16 {
+        self.0 << 3
+    }
+}
+
+impl core::fmt::Display for IpFragOffset {
+    #[inline]
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+impl From<IpFragOffset> for u16 {
+    #[inline]
+    fn from(value: IpFragOffset) -> Self {
+        value.0
+    }
+}
+
+impl TryFrom<u16> for IpFragOffset {
+    type Error = ValueTooBigError<u16>;
+
+    #[inline]
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        use crate::err::ValueType::IpFragmentOffset;
+        if value <= IpFragOffset::MAX_U16 {
+            Ok(IpFragOffset(value))
+        } else {
+            Err(Self::Error {
+                actual: value,
+                max_allowed: IpFragOffset::MAX_U16,
+                value_type: IpFragmentOffset,
+            })
+        }
+    }
+}
+
+#[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 = IpFragOffset(123);
+            let b = a;
+            assert_eq!(a, b);
+            assert_eq!(a.clone(), a);
+        }
+
+        // default
+        {
+            let actual: IpFragOffset = Default::default();
+            assert_eq!(actual.value(), 0);
+        }
+
+        // debug
+        {
+            let a = IpFragOffset(123);
+            assert_eq!(format!("{:?}", a), format!("IpFragOffset(123)"));
+        }
+
+        // ord & partial ord
+        {
+            use core::cmp::Ordering;
+            let a = IpFragOffset(123);
+            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();
+                IpFragOffset(123).hash(&mut hasher);
+                hasher.finish()
+            };
+            let b = {
+                let mut hasher = DefaultHasher::new();
+                IpFragOffset(123).hash(&mut hasher);
+                hasher.finish()
+            };
+            assert_eq!(a, b);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn try_new(
+            valid_value in 0..=0b0001_1111_1111_1111u16,
+            invalid_value in 0b0010_0000_0000_0000u16..=u16::MAX
+        ) {
+            use crate::err::{ValueType, ValueTooBigError};
+            assert_eq!(
+                valid_value,
+                IpFragOffset::try_new(valid_value).unwrap().value()
+            );
+            assert_eq!(
+                IpFragOffset::try_new(invalid_value).unwrap_err(),
+                ValueTooBigError{
+                    actual: invalid_value,
+                    max_allowed: 0b0001_1111_1111_1111,
+                    value_type:  ValueType::IpFragmentOffset
+                }
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn try_from(
+            valid_value in 0..=0b0001_1111_1111_1111u16,
+            invalid_value in 0b0010_0000_0000_0000u16..=u16::MAX
+        ) {
+            use crate::err::{ValueType, ValueTooBigError};
+            // try_into
+            {
+                let actual: IpFragOffset = valid_value.try_into().unwrap();
+                assert_eq!(actual.value(), valid_value);
+
+                let err: Result<IpFragOffset, ValueTooBigError<u16>> = invalid_value.try_into();
+                assert_eq!(
+                    err.unwrap_err(),
+                    ValueTooBigError{
+                        actual: invalid_value,
+                        max_allowed: 0b0001_1111_1111_1111,
+                        value_type:  ValueType::IpFragmentOffset
+                    }
+                );
+            }
+            // try_from
+            {
+                assert_eq!(
+                    IpFragOffset::try_from(valid_value).unwrap().value(),
+                    valid_value
+                );
+
+                assert_eq!(
+                    IpFragOffset::try_from(invalid_value).unwrap_err(),
+                    ValueTooBigError{
+                        actual: invalid_value,
+                        max_allowed: 0b0001_1111_1111_1111,
+                        value_type:  ValueType::IpFragmentOffset
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn new_unchecked(valid_value in 0..=0b0001_1111_1111_1111u16) {
+            assert_eq!(
+                valid_value,
+                unsafe {
+                    IpFragOffset::new_unchecked(valid_value).value()
+                }
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn fmt(valid_value in 0..=0b0001_1111_1111_1111u16) {
+            assert_eq!(format!("{}", IpFragOffset(valid_value)), format!("{}", valid_value));
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from(valid_value in 0..=0b0001_1111_1111_1111u16,) {
+            let frag_offset = IpFragOffset::try_new(valid_value).unwrap();
+            let actual: u16 = frag_offset.into();
+            assert_eq!(actual, valid_value);
+        }
+    }
+}
diff --git a/src/net/ip_headers.rs b/src/net/ip_headers.rs
new file mode 100644
index 0000000..7b901bc
--- /dev/null
+++ b/src/net/ip_headers.rs
@@ -0,0 +1,2642 @@
+#[cfg(feature = "std")]
+use crate::err::ip::HeadersWriteError;
+use crate::err::{Layer, LenError, ValueTooBigError};
+use crate::*;
+
+/// Internet protocol headers version 4 & 6.
+#[derive(Clone, Debug, Eq, PartialEq)]
+#[allow(clippy::large_enum_variant)]
+pub enum IpHeaders {
+    /// IPv4 header & extension headers.
+    Ipv4(Ipv4Header, Ipv4Extensions),
+    /// IPv6 header & extension headers.
+    Ipv6(Ipv6Header, Ipv6Extensions),
+}
+
+impl IpHeaders {
+    /// Maximum summed up length of all extension headers in bytes/octets.
+    pub const MAX_LEN: usize = Ipv6Header::LEN + Ipv6Extensions::MAX_LEN;
+
+    /// Returns references to the IPv4 header & extensions if the header contains IPv4 values.
+    pub fn ipv4(&self) -> Option<(&Ipv4Header, &Ipv4Extensions)> {
+        if let IpHeaders::Ipv4(header, exts) = self {
+            Some((header, exts))
+        } else {
+            None
+        }
+    }
+
+    /// Returns references to the IPv6 header & extensions if the header contains IPv6 values.
+    pub fn ipv6(&self) -> Option<(&Ipv6Header, &Ipv6Extensions)> {
+        if let IpHeaders::Ipv6(header, exts) = self {
+            Some((header, exts))
+        } else {
+            None
+        }
+    }
+
+    /// Renamed to [`IpHeaders::from_slice`]
+    #[deprecated(since = "0.10.1", note = "Renamed to `IpHeaders::from_slice`")]
+    #[inline]
+    pub fn read_from_slice(
+        slice: &[u8],
+    ) -> Result<(IpHeaders, IpNumber, &[u8]), err::ip::HeadersSliceError> {
+        let (header, payload) = IpHeaders::from_slice(slice)?;
+        Ok((header, payload.ip_number, payload.payload))
+    }
+
+    /// Read an [`IpHeaders`] from a slice and return the headers & payload of
+    /// the IP packet (determined based on the length fields in the IP header).
+    ///
+    /// Note that his function returns an [`crate::err::LenError`] if the given slice
+    /// contains less data then the length fields in the IP header indicate should
+    /// be present.
+    ///
+    /// If you want to ignore these kind of length errors based on the length
+    /// fields in the IP headers use [`IpHeaders::from_slice_lax`] instead.
+    pub fn from_slice(
+        slice: &[u8],
+    ) -> Result<(IpHeaders, IpPayloadSlice<'_>), err::ip::HeadersSliceError> {
+        use err::ip::{HeaderError::*, HeadersError::*, HeadersSliceError::*};
+
+        if slice.is_empty() {
+            Err(Len(err::LenError {
+                required_len: 1,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::IpHeader,
+                layer_start_offset: 0,
+            }))
+        } else {
+            match slice[0] >> 4 {
+                4 => {
+                    // check length
+                    if slice.len() < Ipv4Header::MIN_LEN {
+                        return Err(Len(err::LenError {
+                            required_len: Ipv4Header::MIN_LEN,
+                            len: slice.len(),
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv4Header,
+                            layer_start_offset: 0,
+                        }));
+                    }
+
+                    // read ihl
+                    //
+                    // SAFETY:
+                    // Safe as the slice length is checked to be at least
+                    // Ipv4Header::MIN_LEN (20) at the start.
+                    let ihl = unsafe { slice.get_unchecked(0) } & 0xf;
+
+                    //check that the ihl is correct
+                    if ihl < 5 {
+                        return Err(Content(Ip(Ipv4HeaderLengthSmallerThanHeader { ihl })));
+                    }
+
+                    // check that the slice contains enough data for the entire header + options
+                    let header_len = usize::from(ihl) * 4;
+                    if slice.len() < header_len {
+                        return Err(Len(LenError {
+                            required_len: header_len,
+                            len: slice.len(),
+                            len_source: LenSource::Slice,
+                            layer: Layer::Ipv4Header,
+                            layer_start_offset: 0,
+                        }));
+                    }
+
+                    let header = unsafe {
+                        // SAFETY: Safe as the IHL & slice len has been validated
+                        Ipv4HeaderSlice::from_slice_unchecked(core::slice::from_raw_parts(
+                            slice.as_ptr(),
+                            header_len,
+                        ))
+                        .to_header()
+                    };
+
+                    // check that the total len is at least containing the header len
+                    let total_len: usize = header.total_len.into();
+                    if total_len < header_len {
+                        return Err(Len(LenError {
+                            required_len: header_len,
+                            len: total_len,
+                            len_source: LenSource::Ipv4HeaderTotalLen,
+                            layer: Layer::Ipv4Packet,
+                            layer_start_offset: 0,
+                        }));
+                    }
+
+                    // restrict the rest of the slice based on the total len
+                    let rest = if slice.len() < total_len {
+                        return Err(Len(LenError {
+                            required_len: total_len,
+                            len: slice.len(),
+                            len_source: LenSource::Slice,
+                            layer: Layer::Ipv4Packet,
+                            layer_start_offset: 0,
+                        }));
+                    } else {
+                        unsafe {
+                            core::slice::from_raw_parts(
+                                // SAFETY: Safe as the slice length was validated to be at least header_length
+                                slice.as_ptr().add(header_len),
+                                // SAFETY: Safe as slice length has been validated to be at least total_length_usize long
+                                total_len - header_len,
+                            )
+                        }
+                    };
+
+                    let (exts, next_protocol, rest) =
+                        Ipv4Extensions::from_slice(header.protocol, rest).map_err(|err| {
+                            use err::ip_auth::HeaderSliceError as I;
+                            match err {
+                                I::Len(mut err) => {
+                                    err.layer_start_offset += header_len;
+                                    err.len_source = LenSource::Ipv4HeaderTotalLen;
+                                    Len(err)
+                                }
+                                I::Content(err) => Content(Ipv4Ext(err)),
+                            }
+                        })?;
+
+                    let fragmented = header.is_fragmenting_payload();
+                    Ok((
+                        IpHeaders::Ipv4(header, exts),
+                        IpPayloadSlice {
+                            ip_number: next_protocol,
+                            fragmented,
+                            len_source: LenSource::Ipv4HeaderTotalLen,
+                            payload: rest,
+                        },
+                    ))
+                }
+                6 => {
+                    if slice.len() < Ipv6Header::LEN {
+                        return Err(Len(err::LenError {
+                            required_len: Ipv6Header::LEN,
+                            len: slice.len(),
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv6Header,
+                            layer_start_offset: 0,
+                        }));
+                    }
+                    let header = {
+                        // SAFETY:
+                        // This is safe as the slice length is checked to be
+                        // at least Ipv6Header::LEN (40) before this code block.
+                        unsafe {
+                            Ipv6HeaderSlice::from_slice_unchecked(core::slice::from_raw_parts(
+                                slice.as_ptr(),
+                                Ipv6Header::LEN,
+                            ))
+                            .to_header()
+                        }
+                    };
+
+                    // restrict slice by the length specified in the header
+                    let (header_payload, len_source) =
+                        if 0 == header.payload_length && slice.len() > Ipv6Header::LEN {
+                            // In case the payload_length is 0 assume that the entire
+                            // rest of the slice is part of the packet until the jumbogram
+                            // parameters can be parsed.
+
+                            // TODO: Add payload length parsing from the jumbogram
+                            unsafe {
+                                (
+                                    core::slice::from_raw_parts(
+                                        slice.as_ptr().add(Ipv6Header::LEN),
+                                        slice.len() - Ipv6Header::LEN,
+                                    ),
+                                    LenSource::Slice,
+                                )
+                            }
+                        } else {
+                            let payload_len: usize = header.payload_length.into();
+                            let expected_len = Ipv6Header::LEN + payload_len;
+                            if slice.len() < expected_len {
+                                return Err(Len(LenError {
+                                    required_len: expected_len,
+                                    len: slice.len(),
+                                    len_source: LenSource::Slice,
+                                    layer: Layer::Ipv6Packet,
+                                    layer_start_offset: 0,
+                                }));
+                            } else {
+                                unsafe {
+                                    (
+                                        core::slice::from_raw_parts(
+                                            slice.as_ptr().add(Ipv6Header::LEN),
+                                            payload_len,
+                                        ),
+                                        LenSource::Ipv6HeaderPayloadLen,
+                                    )
+                                }
+                            }
+                        };
+
+                    let (exts, next_header, rest) =
+                        Ipv6Extensions::from_slice(header.next_header, header_payload).map_err(
+                            |err| {
+                                use err::ipv6_exts::HeaderSliceError as I;
+                                match err {
+                                    I::Len(mut err) => {
+                                        err.layer_start_offset += Ipv6Header::LEN;
+                                        err.len_source = len_source;
+                                        Len(err)
+                                    }
+                                    I::Content(err) => Content(Ipv6Ext(err)),
+                                }
+                            },
+                        )?;
+
+                    let fragmented = exts.is_fragmenting_payload();
+                    Ok((
+                        IpHeaders::Ipv6(header, exts),
+                        IpPayloadSlice {
+                            ip_number: next_header,
+                            fragmented,
+                            len_source,
+                            payload: rest,
+                        },
+                    ))
+                }
+                version_number => Err(Content(Ip(UnsupportedIpVersion { version_number }))),
+            }
+        }
+    }
+
+    /// Reads an [`IpHeaders`] as far as possible without encountering an error &
+    /// separates the payload from the given slice with less strict length checks.
+    /// This function is usefull for cut off packet or for packets with unset length
+    /// fields).
+    ///
+    /// If you want to only receive correct IpPayloads use [`IpHeaders::from_slice`]
+    /// instead.
+    ///
+    /// The main usecases 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.
+    /// * Parsing packets where the `total_len` (for IPv4) or `payload_length` (for IPv6)
+    ///   have not yet been set. This can be useful when parsing packets which have been
+    ///   recorded in a layer before the length field was set (e.g. before the operating
+    ///   system set the length fields).
+    ///
+    /// # Differences to `from_slice`:
+    ///
+    /// There are two main differences:
+    ///
+    /// * Errors in the expansion headers will only stop the parsing and return an `Ok`
+    ///   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.
+    /// * `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
+    ///   used as a substitute.
+    ///
+    /// You can check if the slice length was used as a substitude 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`] or
+    /// [`LenSource::Ipv6HeaderPayloadLen`].
+    ///
+    /// # When is the slice length used as a fallback?
+    ///
+    /// For IPv4 packets the slice length is used as a fallback/substitude
+    /// if the `total_length` field in the IPv4 header is:
+    ///
+    ///  * Bigger then the given slice (payload cannot fully be seperated).
+    ///  * Too small to contain at least the IPv4 header.
+    ///
+    /// For IPv6 packet the slice length is used as a fallback/substitude
+    /// if the `payload_length` is
+    ///
+    /// * Bigger then the given slice (payload cannot fully be seperated).
+    /// * The value `0`.
+    pub fn from_slice_lax(
+        slice: &[u8],
+    ) -> Result<
+        (
+            IpHeaders,
+            LaxIpPayloadSlice<'_>,
+            Option<(err::ip_exts::HeadersSliceError, Layer)>,
+        ),
+        err::ip::LaxHeaderSliceError,
+    > {
+        use err::ip::{HeaderError::*, LaxHeaderSliceError::*};
+
+        if slice.is_empty() {
+            Err(Len(err::LenError {
+                required_len: 1,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::IpHeader,
+                layer_start_offset: 0,
+            }))
+        } else {
+            match slice[0] >> 4 {
+                4 => {
+                    // check length
+                    if slice.len() < Ipv4Header::MIN_LEN {
+                        return Err(Len(err::LenError {
+                            required_len: Ipv4Header::MIN_LEN,
+                            len: slice.len(),
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv4Header,
+                            layer_start_offset: 0,
+                        }));
+                    }
+
+                    // read ihl
+                    //
+                    // SAFETY:
+                    // Safe as the slice length is checked to be at least
+                    // Ipv4Header::MIN_LEN (20) at the start.
+                    let ihl = unsafe { slice.get_unchecked(0) } & 0xf;
+
+                    //check that the ihl is correct
+                    if ihl < 5 {
+                        return Err(Content(Ipv4HeaderLengthSmallerThanHeader { ihl }));
+                    }
+
+                    // check that the slice contains enough data for the entire header + options
+                    let header_len = usize::from(ihl) * 4;
+                    if slice.len() < header_len {
+                        return Err(Len(LenError {
+                            required_len: header_len,
+                            len: slice.len(),
+                            len_source: LenSource::Slice,
+                            layer: Layer::Ipv4Header,
+                            layer_start_offset: 0,
+                        }));
+                    }
+
+                    let header = unsafe {
+                        // SAFETY: Safe as the IHL & slice len has been validated
+                        Ipv4HeaderSlice::from_slice_unchecked(core::slice::from_raw_parts(
+                            slice.as_ptr(),
+                            header_len,
+                        ))
+                        .to_header()
+                    };
+
+                    // check that the total len is at least containing the header len
+                    let total_len: usize = header.total_len.into();
+
+                    // restrict the rest of the slice based on the total len (if the total_len is not conflicting)
+                    let (len_source, rest, incomplete) = if total_len < header_len {
+                        // fallback to slice len
+                        (
+                            LenSource::Slice,
+                            unsafe {
+                                core::slice::from_raw_parts(
+                                    // SAFETY: Safe as the slice length was validated to be at least header_length
+                                    slice.as_ptr().add(header_len),
+                                    // SAFETY: Safe as slice length has been validated to be at least header_len long
+                                    slice.len() - header_len,
+                                )
+                            },
+                            false,
+                        )
+                    } else if slice.len() < total_len {
+                        (
+                            LenSource::Slice,
+                            unsafe {
+                                core::slice::from_raw_parts(
+                                    // SAFETY: Safe as the slice length was validated to be at least header_length
+                                    slice.as_ptr().add(header_len),
+                                    // SAFETY: Safe as slice length has been validated to be at least header_len long
+                                    slice.len() - header_len,
+                                )
+                            },
+                            true,
+                        )
+                    } else {
+                        (
+                            LenSource::Ipv4HeaderTotalLen,
+                            unsafe {
+                                core::slice::from_raw_parts(
+                                    // SAFETY: Safe as the slice length was validated to be at least header_length
+                                    slice.as_ptr().add(header_len),
+                                    // SAFETY: Safe as slice length has been validated to be at least total_length_usize long
+                                    total_len - header_len,
+                                )
+                            },
+                            false,
+                        )
+                    };
+
+                    let (exts, next_protocol, rest, stop_err) =
+                        Ipv4Extensions::from_slice_lax(header.protocol, rest);
+
+                    let stop_err = stop_err.map(|err| {
+                        use err::ip_auth::HeaderSliceError as I;
+                        use err::ip_exts::HeaderError as OC;
+                        use err::ip_exts::HeadersSliceError as O;
+                        match err {
+                            I::Len(mut l) => O::Len({
+                                l.layer_start_offset += header_len;
+                                l.len_source = len_source;
+                                l
+                            }),
+                            I::Content(c) => O::Content(OC::Ipv4Ext(c)),
+                        }
+                    });
+
+                    let fragmented = header.is_fragmenting_payload();
+                    Ok((
+                        IpHeaders::Ipv4(header, exts),
+                        LaxIpPayloadSlice {
+                            incomplete,
+                            ip_number: next_protocol,
+                            fragmented,
+                            len_source,
+                            payload: rest,
+                        },
+                        stop_err.map(|v| (v, Layer::IpAuthHeader)),
+                    ))
+                }
+                6 => {
+                    if slice.len() < Ipv6Header::LEN {
+                        return Err(Len(err::LenError {
+                            required_len: Ipv6Header::LEN,
+                            len: slice.len(),
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv6Header,
+                            layer_start_offset: 0,
+                        }));
+                    }
+                    let header = {
+                        // SAFETY:
+                        // This is safe as the slice length is checked to be
+                        // at least Ipv6Header::LEN (40) befpre this code block.
+                        unsafe {
+                            Ipv6HeaderSlice::from_slice_unchecked(core::slice::from_raw_parts(
+                                slice.as_ptr(),
+                                Ipv6Header::LEN,
+                            ))
+                            .to_header()
+                        }
+                    };
+
+                    // restrict slice by the length specified in the header
+                    let payload_len = usize::from(header.payload_length);
+                    let (header_payload, len_source, incomplete) =
+                        if (header.payload_length == 0) && (Ipv6Header::LEN < slice.len()) {
+                            // TODO: Add payload length parsing from the jumbogram
+                            unsafe {
+                                (
+                                    core::slice::from_raw_parts(
+                                        // SAFTEY: Safe as we verify what `slice.len() >= Ipv6Header::LEN` above.
+                                        slice.as_ptr().add(Ipv6Header::LEN),
+                                        // SAFTEY: Safe as we verify what `slice.len() >= Ipv6Header::LEN` above.
+                                        slice.len() - Ipv6Header::LEN,
+                                    ),
+                                    LenSource::Slice,
+                                    false,
+                                )
+                            }
+                        } else if (slice.len() - Ipv6Header::LEN) < payload_len {
+                            unsafe {
+                                (
+                                    core::slice::from_raw_parts(
+                                        // SAFTEY: Safe as we verify what `slice.len() >= Ipv6Header::LEN` above.
+                                        slice.as_ptr().add(Ipv6Header::LEN),
+                                        // SAFTEY: Safe as we verify what `slice.len() >= Ipv6Header::LEN` above.
+                                        slice.len() - Ipv6Header::LEN,
+                                    ),
+                                    LenSource::Slice,
+                                    true,
+                                )
+                            }
+                        } else {
+                            unsafe {
+                                (
+                                    core::slice::from_raw_parts(
+                                        // SAFTEY: Safe as we verify what `slice.len() >= Ipv6Header::LEN` above.
+                                        slice.as_ptr().add(Ipv6Header::LEN),
+                                        // SAFTEY: Safe as we verify that `(slice.len() - Ipv6Header::LEN) >= payload_len` above.
+                                        payload_len,
+                                    ),
+                                    LenSource::Ipv6HeaderPayloadLen,
+                                    false,
+                                )
+                            }
+                        };
+
+                    let (exts, next_header, rest, stop_err) =
+                        Ipv6Extensions::from_slice_lax(header.next_header, header_payload);
+
+                    let stop_err = stop_err.map(|(err, layer)| {
+                        use err::ip_exts::HeaderError::Ipv6Ext;
+                        use err::ip_exts::HeadersSliceError as O;
+                        use err::ipv6_exts::HeaderSliceError as I;
+                        (
+                            match err {
+                                I::Len(mut l) => {
+                                    l.layer_start_offset += Ipv6Header::LEN;
+                                    l.len_source = len_source;
+                                    O::Len(l)
+                                }
+                                I::Content(c) => O::Content(Ipv6Ext(c)),
+                            },
+                            layer,
+                        )
+                    });
+
+                    let fragmented = exts.is_fragmenting_payload();
+                    Ok((
+                        IpHeaders::Ipv6(header, exts),
+                        LaxIpPayloadSlice {
+                            incomplete,
+                            ip_number: next_header,
+                            fragmented,
+                            len_source,
+                            payload: rest,
+                        },
+                        stop_err,
+                    ))
+                }
+                version_number => Err(Content(UnsupportedIpVersion { version_number })),
+            }
+        }
+    }
+
+    /// Read an IPv4 header & extension headers from a slice and return the slice containing the payload
+    /// according to the total_length field in the IPv4 header.
+    ///
+    /// Note that his function returns an [`err::LenError`] if the given slice
+    /// contains less data then the `total_len` field in the IPv4 header indicates
+    /// should be present.
+    ///
+    /// If you want to ignore these kind of length errors based on the length
+    /// fields in the IP headers use [`IpHeaders::from_ipv4_slice_lax`] instead.
+    pub fn from_ipv4_slice(
+        slice: &[u8],
+    ) -> Result<(IpHeaders, IpPayloadSlice<'_>), err::ipv4::SliceError> {
+        use err::ipv4::SliceError::*;
+
+        // read the header
+        let (header, header_rest) = Ipv4Header::from_slice(slice).map_err(|err| {
+            use err::ipv4::HeaderSliceError as I;
+            match err {
+                I::Len(err) => Len(err),
+                I::Content(err) => Header(err),
+            }
+        })?;
+
+        // check that the total length at least contains the header
+        let total_len: usize = header.total_len.into();
+        let header_len = header.header_len();
+        let payload_len = if total_len >= header_len {
+            total_len - header_len
+        } else {
+            return Err(Len(LenError {
+                required_len: header_len,
+                len: total_len,
+                len_source: LenSource::Ipv4HeaderTotalLen,
+                layer: Layer::Ipv4Packet,
+                layer_start_offset: 0,
+            }));
+        };
+
+        // limit rest based on ipv4 total length
+        let header_rest = if payload_len > header_rest.len() {
+            return Err(Len(err::LenError {
+                required_len: total_len,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: Layer::Ipv4Packet,
+                layer_start_offset: 0,
+            }));
+        } else {
+            unsafe {
+                // Safe as the payload_len <= header_rest.len is verified above
+                core::slice::from_raw_parts(header_rest.as_ptr(), payload_len)
+            }
+        };
+
+        // read the extension header
+        let (exts, next_header, exts_rest) =
+            Ipv4Extensions::from_slice(header.protocol, header_rest).map_err(|err| {
+                use err::ip_auth::HeaderSliceError as I;
+                match err {
+                    I::Len(mut err) => {
+                        err.layer_start_offset += header.header_len();
+                        err.len_source = LenSource::Ipv4HeaderTotalLen;
+                        Len(err)
+                    }
+                    I::Content(err) => Exts(err),
+                }
+            })?;
+
+        let fragmented = header.is_fragmenting_payload();
+        Ok((
+            IpHeaders::Ipv4(header, exts),
+            IpPayloadSlice {
+                ip_number: next_header,
+                fragmented,
+                len_source: LenSource::Ipv4HeaderTotalLen,
+                payload: exts_rest,
+            },
+        ))
+    }
+
+    /// Reads an IPv4 header & its extensions headers as far as is possible without encountering an
+    /// error & separates the payload from the given slice with less strict length checks.
+    /// This function is usefull for cut off packet or for packets with unset length fields).
+    ///
+    /// If you want to only receive correct IpPayloads use [`IpHeaders::from_ipv4_slice`]
+    /// instead.
+    ///
+    /// The main usecases 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.
+    /// * Parsing packets where the `total_len` (for IPv4) have not yet been set.
+    ///   This can be useful when parsing packets which have been recorded in a
+    ///   layer before the length field was set (e.g. before the operating
+    ///   system set the length fields).
+    ///
+    /// # Differences to `from_ipv4_slice`:
+    ///
+    /// There are two main differences:
+    ///
+    /// * Errors in the expansion headers will only stop the parsing and return an `Ok`
+    ///   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.
+    /// * `from_ipv4_slice_lax` ignores inconsistent `total_len` values. When the `total_len`
+    ///   value in the IPv4 header are inconsistant 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
+    /// if the `len_source` value in the returned [`crate::LaxIpPayloadSlice`] is set to
+    /// [`LenSource::Slice`]. If a substitution was not needed `len_source`
+    /// is set to [`LenSource::Ipv4HeaderTotalLen`].
+    ///
+    /// # When is the slice length used as a fallback?
+    ///
+    /// For IPv4 packets the slice length is used as a fallback/substitude
+    /// if the `total_length` field in the IPv4 header is:
+    ///
+    ///  * Bigger then the given slice (payload cannot fully be seperated).
+    ///  * Too small to contain at least the IPv4 header.
+    pub fn from_ipv4_slice_lax(
+        slice: &[u8],
+    ) -> Result<
+        (
+            IpHeaders,
+            LaxIpPayloadSlice<'_>,
+            Option<err::ip_auth::HeaderSliceError>,
+        ),
+        err::ip::LaxHeaderSliceError,
+    > {
+        use err::ip::LaxHeaderSliceError::*;
+
+        // read the header
+        let (header, header_rest) = Ipv4Header::from_slice(slice).map_err(|err| {
+            use err::ip::HeaderError as O;
+            use err::ipv4::HeaderError as I1;
+            use err::ipv4::HeaderSliceError as I0;
+            match err {
+                I0::Len(err) => Len(err),
+                I0::Content(err) => Content(match err {
+                    I1::UnexpectedVersion { version_number } => {
+                        O::UnsupportedIpVersion { version_number }
+                    }
+                    I1::HeaderLengthSmallerThanHeader { ihl } => {
+                        O::Ipv4HeaderLengthSmallerThanHeader { ihl }
+                    }
+                }),
+            }
+        })?;
+
+        // check that the total len is at least containing the header len
+        let total_len: usize = header.total_len.into();
+
+        // restrict the rest of the slice based on the total len (if the total_len is not conflicting)
+        let header_len = header.header_len();
+        let (len_source, header_rest, incomplete) = if total_len < header_len {
+            // fallback to the rest of the slice
+            (LenSource::Slice, header_rest, false)
+        } else if header_rest.len() < total_len - header_len {
+            // fallback to the rest of the slice
+            (LenSource::Slice, header_rest, true)
+        } else {
+            (
+                LenSource::Ipv4HeaderTotalLen,
+                unsafe {
+                    core::slice::from_raw_parts(
+                        header_rest.as_ptr(),
+                        // SAFETY: Safe as slice length has been validated to be at least total_length_usize long
+                        total_len - header_len,
+                    )
+                },
+                false,
+            )
+        };
+
+        let (exts, next_protocol, payload, mut stop_err) =
+            Ipv4Extensions::from_slice_lax(header.protocol, header_rest);
+
+        use err::ip_auth::HeaderSliceError as I;
+        if let Some(I::Len(err)) = stop_err.as_mut() {
+            err.layer_start_offset += header.header_len();
+            err.len_source = len_source;
+        }
+
+        let fragmented = header.is_fragmenting_payload();
+        Ok((
+            IpHeaders::Ipv4(header, exts),
+            LaxIpPayloadSlice {
+                incomplete,
+                ip_number: next_protocol,
+                fragmented,
+                len_source,
+                payload,
+            },
+            stop_err,
+        ))
+    }
+
+    /// Read an IPv6 header & extension headers from a slice and return the slice
+    /// containing the payload (e.g. TCP, UDP etc.) length limited by payload_length
+    /// field in the IPv6 header.
+    ///
+    /// Note that slice length is used as a fallback value in case the
+    /// payload_length in the IPv6 is set to zero. This is a temporary workaround
+    /// to partially support jumbograms.
+    pub fn from_ipv6_slice(
+        slice: &[u8],
+    ) -> Result<(IpHeaders, IpPayloadSlice<'_>), err::ipv6::SliceError> {
+        use err::ipv6::SliceError::*;
+
+        // read ipv6 header
+        let (header, header_rest) = Ipv6Header::from_slice(slice).map_err(|err| {
+            use err::ipv6::HeaderSliceError as I;
+            match err {
+                I::Len(err) => Len(err),
+                I::Content(err) => Header(err),
+            }
+        })?;
+
+        // restrict slice by the length specified in the header
+        let (header_payload, len_source) =
+            if 0 == header.payload_length && slice.len() > Ipv6Header::LEN {
+                // In case the payload_length is 0 assume that the entire
+                // rest of the slice is part of the packet until the jumbogram
+                // parameters can be parsed.
+
+                // TODO: Add payload length parsing from the jumbogram
+                (header_rest, LenSource::Slice)
+            } else {
+                let payload_len: usize = header.payload_length.into();
+                if header_rest.len() < payload_len {
+                    return Err(Len(LenError {
+                        required_len: payload_len + Ipv6Header::LEN,
+                        len: slice.len(),
+                        len_source: LenSource::Slice,
+                        layer: Layer::Ipv6Packet,
+                        layer_start_offset: 0,
+                    }));
+                } else {
+                    unsafe {
+                        (
+                            core::slice::from_raw_parts(header_rest.as_ptr(), payload_len),
+                            LenSource::Ipv6HeaderPayloadLen,
+                        )
+                    }
+                }
+            };
+
+        // read ipv6 extensions headers
+        let (exts, next_header, exts_rest) =
+            Ipv6Extensions::from_slice(header.next_header, header_payload).map_err(|err| {
+                use err::ipv6_exts::HeaderSliceError as I;
+                match err {
+                    I::Len(mut err) => {
+                        err.layer_start_offset += Ipv6Header::LEN;
+                        err.len_source = len_source;
+                        Len(err)
+                    }
+                    I::Content(err) => Exts(err),
+                }
+            })?;
+
+        let fragmented = exts.is_fragmenting_payload();
+        Ok((
+            IpHeaders::Ipv6(header, exts),
+            IpPayloadSlice {
+                ip_number: next_header,
+                fragmented,
+                len_source,
+                payload: exts_rest,
+            },
+        ))
+    }
+
+    /// Reads an IPv6 header & its extensions headers as far as is possible without encountering an
+    /// error & separates the payload from the given slice with less strict length checks.
+    /// This function is usefull for cut off packet or for packets with unset length fields).
+    ///
+    /// If you want to only receive correct IpPayloads use [`IpHeaders::from_ipv6_slice`]
+    /// instead.
+    ///
+    /// The main usecases 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.
+    /// * Parsing packets where the `payload_length` (in the IPv6 header) has not
+    ///   yet been set. This can be useful when parsing packets which have been
+    ///   recorded in a layer before the length field was set (e.g. before the operating
+    ///   system set the length fields).
+    ///
+    /// # Differences to `from_slice`:
+    ///
+    /// There are two main differences:
+    ///
+    /// * Errors in the expansion headers will only stop the parsing and return an `Ok`
+    ///   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.
+    /// * `from_slice_lax` ignores inconsistent `payload_length` values. When the
+    ///   `payload_length` value in the IPv6 header is inconsistant 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
+    /// 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::Ipv6HeaderPayloadLen`].
+    ///
+    /// # When is the slice length used as a fallback?
+    ///
+    /// The slice length is used as a fallback/substitude if the `payload_length`
+    /// field in the IPv6 header is
+    ///
+    /// * Bigger then the given slice (payload cannot fully be seperated).
+    /// * The value `0`.
+    pub fn from_ipv6_slice_lax(
+        slice: &[u8],
+    ) -> Result<
+        (
+            IpHeaders,
+            LaxIpPayloadSlice<'_>,
+            Option<(err::ipv6_exts::HeaderSliceError, Layer)>,
+        ),
+        err::ipv6::HeaderSliceError,
+    > {
+        // read ipv6 header
+        let (header, header_rest) = Ipv6Header::from_slice(slice)?;
+
+        // restrict slice by the length specified in the header
+        let payload_len: usize = header.payload_length.into();
+        let (header_payload, len_source, incomplete) =
+            if payload_len == 0 && (false == header_rest.is_empty()) {
+                (header_rest, LenSource::Slice, false)
+            } else if payload_len > header_rest.len() {
+                (header_rest, LenSource::Slice, true)
+            } else {
+                unsafe {
+                    (
+                        core::slice::from_raw_parts(header_rest.as_ptr(), payload_len),
+                        LenSource::Ipv6HeaderPayloadLen,
+                        false,
+                    )
+                }
+            };
+
+        // read ipv6 extensions headers
+        let (exts, next_header, exts_rest, mut stop_err) =
+            Ipv6Extensions::from_slice_lax(header.next_header, header_payload);
+
+        use err::ipv6_exts::HeaderSliceError as I;
+        if let Some((I::Len(err), _)) = stop_err.as_mut() {
+            err.layer_start_offset += Ipv6Header::LEN;
+            err.len_source = len_source;
+        };
+
+        let fragmented = exts.is_fragmenting_payload();
+        Ok((
+            IpHeaders::Ipv6(header, exts),
+            LaxIpPayloadSlice {
+                incomplete,
+                ip_number: next_header,
+                fragmented,
+                len_source,
+                payload: exts_rest,
+            },
+            stop_err,
+        ))
+    }
+
+    /// Reads an IP (v4 or v6) header from the current position (requires
+    /// crate feature `std`).
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut T,
+    ) -> Result<(IpHeaders, IpNumber), err::ip::HeaderReadError> {
+        use crate::io::LimitedReader;
+        use err::ip::{HeaderError::*, HeaderReadError::*, HeadersError::*};
+
+        let value = {
+            let mut buf = [0; 1];
+            reader.read_exact(&mut buf).map_err(Io)?;
+            buf[0]
+        };
+        match value >> 4 {
+            4 => {
+                // get internet header length
+                let ihl = value & 0xf;
+
+                // check that the ihl is correct
+                if ihl < 5 {
+                    return Err(Content(Ip(Ipv4HeaderLengthSmallerThanHeader { ihl })));
+                }
+
+                // read the rest of the header
+                let header_len_u16 = u16::from(ihl) * 4;
+                let header_len = usize::from(header_len_u16);
+                let mut buffer = [0u8; Ipv4Header::MAX_LEN];
+                buffer[0] = value;
+                reader.read_exact(&mut buffer[1..header_len]).map_err(Io)?;
+
+                let header = unsafe {
+                    // SAFETY: Safe as both the IHL and slice len have been verified
+                    Ipv4HeaderSlice::from_slice_unchecked(&buffer[..header_len])
+                }
+                .to_header();
+
+                // check that the total len is long enough to contain the header
+                let total_len = usize::from(header.total_len);
+                let mut reader = if total_len < header_len {
+                    return Err(Len(LenError {
+                        required_len: header_len,
+                        len: total_len,
+                        len_source: LenSource::Ipv4HeaderTotalLen,
+                        layer: Layer::Ipv4Packet,
+                        layer_start_offset: 0,
+                    }));
+                } else {
+                    // create a new reader that is limited by the total_len value length
+                    LimitedReader::new(
+                        reader,
+                        total_len - header_len,
+                        LenSource::Ipv4HeaderTotalLen,
+                        header_len,
+                        Layer::Ipv4Header,
+                    )
+                };
+
+                // read the extension headers if present
+                Ipv4Extensions::read_limited(&mut reader, header.protocol)
+                    .map(|(ext, next)| (IpHeaders::Ipv4(header, ext), next))
+                    .map_err(|err| {
+                        use err::ip_auth::HeaderLimitedReadError as I;
+                        match err {
+                            I::Io(err) => Io(err),
+                            I::Len(err) => Len(err),
+                            I::Content(err) => Content(Ipv4Ext(err)),
+                        }
+                    })
+            }
+            6 => {
+                let header = Ipv6Header::read_without_version(reader, value & 0xf).map_err(Io)?;
+
+                // create a new reader that is limited by the payload_len value length
+                let mut reader = LimitedReader::new(
+                    reader,
+                    header.payload_length.into(),
+                    LenSource::Ipv6HeaderPayloadLen,
+                    header.header_len(),
+                    Layer::Ipv6Header,
+                );
+
+                Ipv6Extensions::read_limited(&mut reader, header.next_header)
+                    .map(|(ext, next)| (IpHeaders::Ipv6(header, ext), next))
+                    .map_err(|err| {
+                        use err::ipv6_exts::HeaderLimitedReadError as I;
+                        match err {
+                            I::Io(err) => Io(err),
+                            I::Len(err) => Len(err),
+                            I::Content(err) => Content(Ipv6Ext(err)),
+                        }
+                    })
+            }
+            version_number => Err(Content(Ip(UnsupportedIpVersion { version_number }))),
+        }
+    }
+
+    /// Writes an IP (v4 or v6) header to the current position (requires
+    /// crate feature `std`).
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn write<T: std::io::Write + Sized>(
+        &self,
+        writer: &mut T,
+    ) -> Result<(), HeadersWriteError> {
+        use crate::IpHeaders::*;
+        use HeadersWriteError::*;
+        match *self {
+            Ipv4(ref header, ref extensions) => {
+                header.write(writer).map_err(Io)?;
+                extensions.write(writer, header.protocol).map_err(|err| {
+                    use err::ipv4_exts::HeaderWriteError as I;
+                    match err {
+                        I::Io(err) => Io(err),
+                        I::Content(err) => Ipv4Exts(err),
+                    }
+                })
+            }
+            Ipv6(ref header, ref extensions) => {
+                header.write(writer).map_err(Io)?;
+                extensions.write(writer, header.next_header).map_err(|err| {
+                    use err::ipv6_exts::HeaderWriteError as I;
+                    match err {
+                        I::Io(err) => Io(err),
+                        I::Content(err) => Ipv6Exts(err),
+                    }
+                })
+            }
+        }
+    }
+
+    /// Returns the size when the ip header & extensions are serialized
+    pub fn header_len(&self) -> usize {
+        use crate::IpHeaders::*;
+        match *self {
+            Ipv4(ref header, ref extensions) => header.header_len() + extensions.header_len(),
+            Ipv6(_, ref extensions) => Ipv6Header::LEN + extensions.header_len(),
+        }
+    }
+
+    /// Returns the last next header number following the ip header
+    /// and header extensions.
+    pub fn next_header(&self) -> Result<IpNumber, err::ip_exts::ExtsWalkError> {
+        use crate::err::ip_exts::ExtsWalkError::*;
+        use crate::IpHeaders::*;
+        match *self {
+            Ipv4(ref header, ref extensions) => {
+                extensions.next_header(header.protocol).map_err(Ipv4Exts)
+            }
+            Ipv6(ref header, ref extensions) => {
+                extensions.next_header(header.next_header).map_err(Ipv6Exts)
+            }
+        }
+    }
+
+    /// Sets all the next_header fields in the ipv4 & ipv6 header
+    /// as well as in all extension headers and returns the ether
+    /// type number.
+    ///
+    /// The given number will be set as the last "next_header" or
+    /// protocol number.
+    pub fn set_next_headers(&mut self, last_next_header: IpNumber) -> u16 {
+        use IpHeaders::*;
+        match self {
+            Ipv4(ref mut header, ref mut extensions) => {
+                header.protocol = extensions.set_next_headers(last_next_header);
+                EtherType::IPV4.0
+            }
+            Ipv6(ref mut header, ref mut extensions) => {
+                header.next_header = extensions.set_next_headers(last_next_header);
+                EtherType::IPV4.0
+            }
+        }
+    }
+
+    /// Tries to set the length field in the ip header given the length of data
+    /// after the ip header and extension header(s).
+    ///
+    /// If the payload length is too large to be stored in the length fields
+    /// of the ip header an error is returned.
+    ///
+    /// Note that this function will automatically add the length of the extension
+    /// headers is they are present.
+    pub fn set_payload_len(&mut self, len: usize) -> Result<(), ValueTooBigError<usize>> {
+        use crate::err::ValueType;
+        match self {
+            IpHeaders::Ipv4(ipv4_hdr, exts) => {
+                if let Some(complete_len) = len.checked_add(exts.header_len()) {
+                    ipv4_hdr.set_payload_len(complete_len)
+                } else {
+                    Err(ValueTooBigError {
+                        actual: len,
+                        max_allowed: usize::from(u16::MAX)
+                            - ipv4_hdr.header_len()
+                            - exts.header_len(),
+                        value_type: ValueType::Ipv4PayloadLength,
+                    })
+                }
+            }
+            IpHeaders::Ipv6(ipv6_hdr, exts) => {
+                if let Some(complete_len) = len.checked_add(exts.header_len()) {
+                    ipv6_hdr.set_payload_length(complete_len)
+                } else {
+                    Err(ValueTooBigError {
+                        actual: len,
+                        max_allowed: usize::from(u16::MAX) - exts.header_len(),
+                        value_type: ValueType::Ipv4PayloadLength,
+                    })
+                }
+            }
+        }
+    }
+
+    /// Returns true if the payload is fragmented based on the IPv4 header
+    /// or the IPv6 fragment header.
+    pub fn is_fragmenting_payload(&self) -> bool {
+        match self {
+            IpHeaders::Ipv4(ipv4, _) => ipv4.is_fragmenting_payload(),
+            IpHeaders::Ipv6(_, exts) => exts.is_fragmenting_payload(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{
+        err::{
+            ip::{HeadersError, HeadersSliceError},
+            Layer, LenError,
+        },
+        ip_number::*,
+        test_gens::*,
+        *,
+    };
+    use alloc::{borrow::ToOwned, format, vec::Vec};
+    use proptest::prelude::*;
+    use std::io::Cursor;
+
+    const EXTENSION_KNOWN_IP_NUMBERS: [IpNumber; 5] = [
+        AUTH,
+        IPV6_DEST_OPTIONS,
+        IPV6_HOP_BY_HOP,
+        IPV6_FRAG,
+        IPV6_ROUTE,
+    ];
+
+    fn combine_v4(v4: &Ipv4Header, ext: &Ipv4Extensions, payload: &[u8]) -> IpHeaders {
+        IpHeaders::Ipv4(
+            {
+                let mut v4 = v4.clone();
+                v4.protocol = if ext.auth.is_some() { AUTH } else { UDP };
+                v4.total_len = (v4.header_len() + ext.header_len() + payload.len()) as u16;
+                v4.header_checksum = v4.calc_header_checksum();
+                v4
+            },
+            ext.clone(),
+        )
+    }
+
+    fn combine_v6(v6: &Ipv6Header, ext: &Ipv6Extensions, payload: &[u8]) -> IpHeaders {
+        let (ext, next_header) = {
+            let mut ext = ext.clone();
+            let next_header = ext.set_next_headers(UDP);
+            (ext, next_header)
+        };
+        IpHeaders::Ipv6(
+            {
+                let mut v6 = v6.clone();
+                v6.next_header = next_header;
+                v6.payload_length = (ext.header_len() + payload.len()) as u16;
+                v6
+            },
+            ext,
+        )
+    }
+
+    proptest! {
+        #[test]
+        fn debug(
+            v4 in ipv4_any(),
+            v4_exts in ipv4_extensions_any(),
+            v6 in ipv6_any(),
+            v6_exts in ipv6_extensions_any(),
+        ) {
+            assert_eq!(
+                format!(
+                    "Ipv4({:?}, {:?})",
+                    v4,
+                    v4_exts
+                ),
+                format!("{:?}", IpHeaders::Ipv4(v4, v4_exts))
+            );
+            assert_eq!(
+                format!(
+                    "Ipv6({:?}, {:?})",
+                    v6,
+                    v6_exts
+                ),
+                format!("{:?}", IpHeaders::Ipv6(v6, v6_exts))
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(
+            v4 in ipv4_any(),
+            v4_exts in ipv4_extensions_any(),
+            v6 in ipv6_any(),
+            v6_exts in ipv6_extensions_any(),
+        ) {
+            {
+                let v4 = IpHeaders::Ipv4(v4, v4_exts);
+                assert_eq!(v4, v4.clone());
+            }
+            {
+                let v6 = IpHeaders::Ipv6(v6, v6_exts);
+                assert_eq!(v6, v6.clone());
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn ipv4(
+            v4 in ipv4_any(),
+            v4_exts in ipv4_extensions_any(),
+            v6 in ipv6_any(),
+            v6_exts in ipv6_extensions_any(),
+        ) {
+            assert_eq!(
+                IpHeaders::Ipv4(v4.clone(), v4_exts.clone()).ipv4(),
+                Some((&v4, &v4_exts))
+            );
+            assert_eq!(
+                IpHeaders::Ipv6(v6.clone(), v6_exts.clone()).ipv4(),
+                None
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn ipv6(
+            v4 in ipv4_any(),
+            v4_exts in ipv4_extensions_any(),
+            v6 in ipv6_any(),
+            v6_exts in ipv6_extensions_any(),
+        ) {
+            assert_eq!(
+                IpHeaders::Ipv4(v4.clone(), v4_exts.clone()).ipv6(),
+                None
+            );
+            assert_eq!(
+                IpHeaders::Ipv6(v6.clone(), v6_exts.clone()).ipv6(),
+                Some((&v6, &v6_exts))
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        #[allow(deprecated)]
+        fn read_from_slice(
+            v4 in ipv4_any(),
+            v4_exts in ipv4_extensions_any(),
+        ) {
+            let header = combine_v4(&v4, &v4_exts, &[]);
+            let mut buffer = Vec::with_capacity(header.header_len());
+            header.write(&mut buffer).unwrap();
+
+            let actual = IpHeaders::read_from_slice(&buffer).unwrap();
+            assert_eq!(actual.0, header);
+            assert_eq!(actual.1, header.next_header().unwrap());
+            assert_eq!(actual.2, &buffer[buffer.len()..]);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn ip_from_slice(
+            v4 in ipv4_any(),
+            v4_exts in ipv4_extensions_any(),
+            v6 in ipv6_any(),
+            v6_exts in ipv6_extensions_any(),
+        ) {
+            use err::ip::{HeadersError::*, HeaderError::*, HeadersSliceError::*};
+
+            // empty error
+            assert_eq!(
+                IpHeaders::from_slice(&[]),
+                Err(Len(err::LenError {
+                    required_len: 1,
+                    len: 0,
+                    len_source: LenSource::Slice,
+                    layer: err::Layer::IpHeader,
+                    layer_start_offset: 0,
+                }))
+            );
+
+            // unknown version
+            for version_number in 0..=0xfu8 {
+                if version_number != 4 && version_number != 6 {
+                    assert_eq!(
+                        IpHeaders::from_slice(&[version_number << 4]),
+                        Err(Content(Ip(UnsupportedIpVersion { version_number })))
+                    );
+                }
+            }
+
+            let payload = [1,2,3,4];
+
+            // v4
+            {
+                let header = combine_v4(&v4, &v4_exts, &payload);
+                let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1);
+                header.write(&mut buffer).unwrap();
+                buffer.extend_from_slice(&payload);
+                buffer.push(1); // add some value to check the return slice
+
+                // read
+                {
+                    let actual = IpHeaders::from_slice(&buffer).unwrap();
+                    assert_eq!(&actual.0, &header);
+                    assert_eq!(
+                        actual.1,
+                        IpPayloadSlice{
+                            ip_number: header.next_header().unwrap(),
+                            fragmented: header.is_fragmenting_payload(),
+                            len_source: LenSource::Ipv4HeaderTotalLen,
+                            payload: &payload
+                        }
+                    );
+                }
+
+                // read error ipv4 header
+                IpHeaders::from_slice(&buffer[..1]).unwrap_err();
+
+                // read error ipv4 extensions
+                if v4_exts.header_len() > 0 {
+                    IpHeaders::from_slice(&buffer[..v4.header_len() + 1]).unwrap_err();
+                }
+
+                // total length smaller the header
+                {
+                    let bad_total_len = (v4.header_len() - 1) as u16;
+
+                    let mut buffer = buffer.clone();
+                    // inject bad total_len
+                    let bad_total_len_be = bad_total_len.to_be_bytes();
+                    buffer[2] = bad_total_len_be[0];
+                    buffer[3] = bad_total_len_be[1];
+                    assert_eq!(
+                        IpHeaders::from_slice(&buffer[..]).unwrap_err(),
+                        HeadersSliceError::Len(LenError{
+                            required_len: v4.header_len(),
+                            len: bad_total_len as usize,
+                            len_source: LenSource::Ipv4HeaderTotalLen,
+                            layer: Layer::Ipv4Packet,
+                            layer_start_offset: 0,
+                        })
+                    );
+                }
+            }
+
+            // v6
+            {
+                let header = combine_v6(&v6, &v6_exts, &payload);
+                let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1);
+                header.write(&mut buffer).unwrap();
+                buffer.extend_from_slice(&payload);
+                buffer.push(1); // add some value to check the return slice
+
+                // len error
+                {
+                    let actual = IpHeaders::from_slice(&buffer).unwrap();
+                    assert_eq!(&actual.0, &header);
+                    assert_eq!(
+                        actual.1,
+                        IpPayloadSlice{
+                            ip_number: header.next_header().unwrap(),
+                            fragmented: header.is_fragmenting_payload(),
+                            len_source: LenSource::Ipv6HeaderPayloadLen,
+                            payload: &payload
+                        }
+                    );
+                }
+
+                // read error header
+                IpHeaders::from_slice(&buffer[..1]).unwrap_err();
+
+                // read error ipv4 extensions
+                if v6_exts.header_len() > 0 {
+                    IpHeaders::from_slice(&buffer[..Ipv6Header::LEN + 1]).unwrap_err();
+                }
+
+                // len error (with payload len zero)
+                if v6_exts.header_len() > 0 {
+                    let mut buffer = buffer.clone();
+
+                    // inject zero as payload len
+                    buffer[4] = 0;
+                    buffer[5] = 0;
+
+                    assert!(
+                        IpHeaders::from_slice(
+                            &buffer[..buffer.len() - payload.len() - 2]
+                        ).is_err()
+                    );
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice_lax(
+            v4 in ipv4_any(),
+            v4_exts in ipv4_extensions_any(),
+            v6 in ipv6_any(),
+            v6_exts in ipv6_extensions_any(),
+        ) {
+            use err::ip::{HeaderError::*, LaxHeaderSliceError::*};
+
+            let payload = [1,2,3,4];
+
+            // empty error
+            assert_eq!(
+                IpHeaders::from_slice_lax(&[]),
+                Err(Len(err::LenError {
+                    required_len: 1,
+                    len: 0,
+                    len_source: LenSource::Slice,
+                    layer: err::Layer::IpHeader,
+                    layer_start_offset: 0,
+                }))
+            );
+
+            // unknown version
+            for version_number in 0..=0xfu8 {
+                if version_number != 4 && version_number != 6 {
+                    assert_eq!(
+                        IpHeaders::from_slice_lax(&[version_number << 4]),
+                        Err(Content(UnsupportedIpVersion { version_number }))
+                    );
+                }
+            }
+
+            // v4
+            {
+                let header = combine_v4(&v4, &v4_exts, &payload);
+                let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1);
+                header.write(&mut buffer).unwrap();
+                buffer.extend_from_slice(&payload);
+                buffer.push(1); // add some value to check the return slice
+
+                // normal read
+                {
+                    let actual = IpHeaders::from_slice_lax(&buffer).unwrap();
+                    assert_eq!(&actual.0, &header);
+                    assert_eq!(
+                        actual.1,
+                        LaxIpPayloadSlice{
+                            incomplete: false,
+                            ip_number: header.next_header().unwrap(),
+                            fragmented: header.is_fragmenting_payload(),
+                            len_source: LenSource::Ipv4HeaderTotalLen,
+                            payload: &payload
+                        }
+                    );
+                }
+
+                // error len smaller then min header len
+                for len in 1..Ipv4Header::MIN_LEN {
+                    assert_eq!(
+                        IpHeaders::from_slice_lax(&buffer[..len]),
+                        Err(Len(err::LenError {
+                            required_len: Ipv4Header::MIN_LEN,
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv4Header,
+                            layer_start_offset: 0,
+                        }))
+                    );
+                }
+
+                // ihl value error
+                {
+                    let mut bad_ihl_buffer = buffer.clone();
+                    for bad_ihl in 0..5 {
+                        bad_ihl_buffer[0] = (bad_ihl_buffer[0] & 0xf0) | bad_ihl;
+                        assert_eq!(
+                            IpHeaders::from_slice_lax(&bad_ihl_buffer),
+                            Err(Content(Ipv4HeaderLengthSmallerThanHeader { ihl: bad_ihl }))
+                        );
+                    }
+                }
+
+                // ihl len error
+                for short_ihl in 5..usize::from(v4.ihl()) {
+                    assert_eq!(
+                        IpHeaders::from_slice_lax(&buffer[..4*short_ihl]),
+                        Err(Len(err::LenError {
+                            required_len: usize::from(v4.ihl())*4,
+                            len: 4*short_ihl,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv4Header,
+                            layer_start_offset: 0,
+                        }))
+                    );
+                }
+
+                // total_len bigger then slice len (fallback to slice len)
+                for payload_len in 0..payload.len(){
+                    let actual = IpHeaders::from_slice_lax(&buffer[..v4.header_len() + v4_exts.header_len() + payload_len]).unwrap();
+                    assert_eq!(&actual.0, &header);
+                    assert_eq!(
+                        actual.1,
+                        LaxIpPayloadSlice{
+                            incomplete: true,
+                            ip_number: header.next_header().unwrap(),
+                            fragmented: header.is_fragmenting_payload(),
+                            len_source: LenSource::Slice,
+                            payload: &payload[..payload_len]
+                        }
+                    );
+                }
+
+                // len error ipv4 extensions
+                if v4_exts.header_len() > 0 {
+                    let (_, _, stop_err) = IpHeaders::from_slice_lax(&buffer[..v4.header_len() + 1]).unwrap();
+                    assert!(stop_err.is_some());
+                }
+
+                // content error ipv4 extensions
+                if v4_exts.auth.is_some() {
+                    use err::ip_auth::HeaderError::ZeroPayloadLen;
+                    use err::ip_exts::HeadersSliceError::Content;
+                    use err::ip_exts::HeaderError::Ipv4Ext;
+
+                    // introduce a auth header zero payload error
+                    let mut errored_buffer = buffer.clone();
+                    // inject length zero into auth header (not valid, will
+                    // trigger a content error)
+                    errored_buffer[v4.header_len() + 1] = 0;
+
+                    let (_, _, stop_err) = IpHeaders::from_slice_lax(&errored_buffer).unwrap();
+
+                    assert_eq!(stop_err, Some((Content(Ipv4Ext(ZeroPayloadLen)), Layer::IpAuthHeader)));
+                }
+
+                // total length smaller the header (fallback to slice len)
+                {
+                    let bad_total_len = (v4.header_len() - 1) as u16;
+
+                    let mut buffer = buffer.clone();
+                    // inject bad total_len
+                    let bad_total_len_be = bad_total_len.to_be_bytes();
+                    buffer[2] = bad_total_len_be[0];
+                    buffer[3] = bad_total_len_be[1];
+
+                    let actual = IpHeaders::from_slice_lax(&buffer[..]).unwrap();
+
+                    let (v4_header, v4_exts) = header.ipv4().unwrap();
+                    let expected_headers = IpHeaders::Ipv4(
+                        {
+                            let mut expected_v4 = v4_header.clone();
+                            expected_v4.total_len = bad_total_len;
+                            expected_v4
+                        },
+                        v4_exts.clone()
+                    );
+                    assert_eq!(&expected_headers, &actual.0);
+                    assert_eq!(
+                        actual.1,
+                        LaxIpPayloadSlice{
+                            incomplete: false,
+                            ip_number: header.next_header().unwrap(),
+                            fragmented: header.is_fragmenting_payload(),
+                            len_source: LenSource::Slice,
+                            payload: &buffer[v4_header.header_len() + v4_exts.header_len()..],
+                        }
+                    );
+                }
+            }
+
+            // v6
+            {
+                let header = combine_v6(&v6, &v6_exts, &payload);
+                let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1);
+                header.write(&mut buffer).unwrap();
+                buffer.extend_from_slice(&payload);
+                buffer.push(1); // add some value to check the return slice
+
+                // normal read
+                {
+                    let actual = IpHeaders::from_slice_lax(&buffer).unwrap();
+                    assert_eq!(&actual.0, &header);
+                    assert_eq!(
+                        actual.1,
+                        LaxIpPayloadSlice{
+                            incomplete: false,
+                            ip_number: header.next_header().unwrap(),
+                            fragmented: header.is_fragmenting_payload(),
+                            len_source: LenSource::Ipv6HeaderPayloadLen,
+                            payload: &payload
+                        }
+                    );
+                }
+
+                // smaller then header
+                for len in 1..Ipv6Header::LEN {
+                    assert_eq!(
+                        IpHeaders::from_slice_lax(&buffer[..len]),
+                        Err(Len(err::LenError {
+                            required_len: Ipv6Header::LEN,
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv6Header,
+                            layer_start_offset: 0,
+                        }))
+                    );
+                }
+
+                // extension len error
+                if v6_exts.header_len() > 0 {
+                    let (actual, _, stop_err) = IpHeaders::from_slice_lax(&buffer[..v6.header_len() + 1]).unwrap();
+                    assert_eq!(&actual.ipv6().as_ref().unwrap().0, &header.ipv6().as_ref().unwrap().0);
+                    assert!(stop_err.is_some());
+                }
+
+                // extension content error
+                if v6_exts.auth.is_some() {
+                    use err::ip_auth::HeaderError::ZeroPayloadLen;
+                    use err::ip_exts::HeadersSliceError::Content;
+                    use err::ip_exts::HeaderError::Ipv6Ext;
+                    use err::ipv6_exts::HeaderError::IpAuth;
+
+                    // introduce a auth header zero payload error
+                    let mut errored_buffer = buffer.clone();
+                    let auth_offset = v6.header_len() +
+                        v6_exts.hop_by_hop_options.as_ref().map(|h| h.header_len()).unwrap_or(0) +
+                        v6_exts.destination_options.as_ref().map(|h| h.header_len()).unwrap_or(0) +
+                        v6_exts.routing.as_ref().map(|h| h.routing.header_len()).unwrap_or(0) +
+                        // routing.final_destination_options skiped, as after auth
+                        v6_exts.fragment.as_ref().map(|h| h.header_len()).unwrap_or(0);
+
+                    // inject length zero into auth header (not valid, will
+                    // trigger a content error)
+                    errored_buffer[auth_offset + 1] = 0;
+
+                    let (_, _, stop_err) = IpHeaders::from_slice_lax(&errored_buffer).unwrap();
+                    assert_eq!(
+                        stop_err,
+                        Some((Content(Ipv6Ext(IpAuth(ZeroPayloadLen))), Layer::IpAuthHeader))
+                    );
+                }
+
+                // slice smaller then payload len
+                for len in (v6.header_len()+v6_exts.header_len())..buffer.len() - 1 {
+                    let actual = IpHeaders::from_slice_lax(&buffer[..len]).unwrap();
+                    assert_eq!(&actual.0, &header);
+                    assert_eq!(
+                        actual.1,
+                        LaxIpPayloadSlice{
+                            incomplete: true,
+                            ip_number: header.next_header().unwrap(),
+                            fragmented: header.is_fragmenting_payload(),
+                            len_source: LenSource::Slice,
+                            payload: &payload[..len - v6.header_len() - v6_exts.header_len()]
+                        }
+                    );
+                }
+
+                // payload len zero (fallback to slice len)
+                {
+                    let mut buffer = buffer.clone();
+                    // inject zero as payload len
+                    buffer[4] = 0;
+                    buffer[5] = 0;
+
+                    let actual = IpHeaders::from_slice_lax(&buffer[..]).unwrap();
+
+                    let (v6_header, v6_exts) = header.ipv6().unwrap();
+                    let expected_headers = IpHeaders::Ipv6(
+                        {
+                            let mut expected_v6 = v6_header.clone();
+                            expected_v6.payload_length = 0;
+                            expected_v6
+                        },
+                        v6_exts.clone()
+                    );
+                    assert_eq!(&expected_headers, &actual.0);
+                    assert_eq!(
+                        actual.1,
+                        LaxIpPayloadSlice{
+                            incomplete: false,
+                            ip_number: header.next_header().unwrap(),
+                            fragmented: header.is_fragmenting_payload(),
+                            len_source: LenSource::Slice,
+                            payload: &buffer[v6_header.header_len() + v6_exts.header_len()..],
+                        }
+                    );
+                }
+
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_ipv4_slice(
+            v4 in ipv4_any(),
+            v4_exts in ipv4_extensions_any(),
+        ) {
+            let payload = [1,2,3,4];
+
+            let header = combine_v4(&v4, &v4_exts, &payload);
+            let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1);
+            header.write(&mut buffer).unwrap();
+            buffer.extend_from_slice(&payload);
+            buffer.push(1); // add some value to check the return slice
+
+            // read
+            {
+                let actual = IpHeaders::from_ipv4_slice(&buffer).unwrap();
+                assert_eq!(&actual.0, &header);
+                assert_eq!(
+                    actual.1,
+                    IpPayloadSlice{
+                        ip_number: header.next_header().unwrap(),
+                        fragmented: header.is_fragmenting_payload(),
+                        len_source: LenSource::Ipv4HeaderTotalLen,
+                        payload: &payload
+                    }
+                );
+            }
+
+            // read error ipv4 header
+            IpHeaders::from_ipv4_slice(&buffer[..1]).unwrap_err();
+
+            // read error ipv4 extensions
+            if v4_exts.header_len() > 0 {
+                IpHeaders::from_ipv4_slice(&buffer[..v4.header_len() + 1]).unwrap_err();
+            }
+
+            // total length smaller the header
+            {
+                let bad_total_len = (v4.header_len() - 1) as u16;
+
+                let mut buffer = buffer.clone();
+                // inject bad total_len
+                let bad_total_len_be = bad_total_len.to_be_bytes();
+                buffer[2] = bad_total_len_be[0];
+                buffer[3] = bad_total_len_be[1];
+                assert_eq!(
+                    IpHeaders::from_ipv4_slice(&buffer[..]).unwrap_err(),
+                    err::ipv4::SliceError::Len(LenError{
+                        required_len: v4.header_len(),
+                        len: bad_total_len as usize,
+                        len_source: LenSource::Ipv4HeaderTotalLen,
+                        layer: Layer::Ipv4Packet,
+                        layer_start_offset: 0,
+                    })
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_ipv4_slice_lax(
+            v4 in ipv4_any(),
+            v4_exts in ipv4_extensions_any()
+        ) {
+            use err::ip::{LaxHeaderSliceError::*, HeaderError::*};
+
+            let payload = [1,2,3,4];
+
+            // empty error
+            assert_eq!(
+                IpHeaders::from_ipv4_slice_lax(&[]),
+                Err(Len(err::LenError {
+                    required_len: 20,
+                    len: 0,
+                    len_source: LenSource::Slice,
+                    layer: err::Layer::Ipv4Header,
+                    layer_start_offset: 0,
+                }))
+            );
+
+            // build a buffer with a valid packet
+            let header = combine_v4(&v4, &v4_exts, &payload);
+            let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1);
+            header.write(&mut buffer).unwrap();
+            buffer.extend_from_slice(&payload);
+            buffer.push(1); // add some value to check the return slice
+
+            // normal read
+            {
+                let actual = IpHeaders::from_ipv4_slice_lax(&buffer).unwrap();
+                assert_eq!(&actual.0, &header);
+                assert_eq!(
+                    actual.1,
+                    LaxIpPayloadSlice{
+                        incomplete: false,
+                        ip_number: header.next_header().unwrap(),
+                        fragmented: header.is_fragmenting_payload(),
+                        len_source: LenSource::Ipv4HeaderTotalLen,
+                        payload: &payload
+                    }
+                );
+            }
+
+            // error len smaller then min header len
+            for len in 1..Ipv4Header::MIN_LEN {
+                assert_eq!(
+                    IpHeaders::from_ipv4_slice_lax(&buffer[..len]),
+                    Err(Len(err::LenError {
+                        required_len: Ipv4Header::MIN_LEN,
+                        len,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::Ipv4Header,
+                        layer_start_offset: 0,
+                    }))
+                );
+            }
+
+            // ihl value error
+            {
+                let mut bad_ihl_buffer = buffer.clone();
+                for bad_ihl in 0..5 {
+                    bad_ihl_buffer[0] = (bad_ihl_buffer[0] & 0xf0) | bad_ihl;
+                    assert_eq!(
+                        IpHeaders::from_ipv4_slice_lax(&bad_ihl_buffer),
+                        Err(Content(Ipv4HeaderLengthSmallerThanHeader { ihl: bad_ihl }))
+                    );
+                }
+            }
+
+            // ihl len error
+            for short_ihl in 5..usize::from(v4.ihl()) {
+                assert_eq!(
+                    IpHeaders::from_ipv4_slice_lax(&buffer[..4*short_ihl]),
+                    Err(Len(err::LenError {
+                        required_len: usize::from(v4.ihl())*4,
+                        len: 4*short_ihl,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::Ipv4Header,
+                        layer_start_offset: 0,
+                    }))
+                );
+            }
+
+            // total_len bigger then slice len (fallback to slice len)
+            for payload_len in 0..payload.len(){
+                let actual = IpHeaders::from_ipv4_slice_lax(&buffer[..v4.header_len() + v4_exts.header_len() + payload_len]).unwrap();
+                assert_eq!(&actual.0, &header);
+                assert_eq!(
+                    actual.1,
+                    LaxIpPayloadSlice{
+                        incomplete: true,
+                        ip_number: header.next_header().unwrap(),
+                        fragmented: header.is_fragmenting_payload(),
+                        len_source: LenSource::Slice,
+                        payload: &payload[..payload_len]
+                    }
+                );
+            }
+
+            // len error ipv4 extensions
+            if v4_exts.header_len() > 0 {
+                let (actual, _, stop_err) = IpHeaders::from_ipv4_slice_lax(&buffer[..v4.header_len() + 1]).unwrap();
+                assert_eq!(actual.ipv4().unwrap().0, header.ipv4().unwrap().0);
+                assert!(stop_err.is_some());
+            }
+
+            // content error ipv4 extensions
+            if v4_exts.auth.is_some() {
+                use err::ip_auth::HeaderSliceError::Content;
+                use err::ip_auth::HeaderError::ZeroPayloadLen;
+
+                // introduce a auth header zero payload error
+                let mut errored_buffer = buffer.clone();
+                // inject length zero into auth header (not valid, will
+                // trigger a content error)
+                errored_buffer[v4.header_len() + 1] = 0;
+
+                let (_, _, stop_err) = IpHeaders::from_ipv4_slice_lax(&errored_buffer).unwrap();
+                assert_eq!(stop_err, Some(Content(ZeroPayloadLen)));
+            }
+
+            // total length smaller the header (fallback to slice len)
+            {
+                let bad_total_len = (v4.header_len() - 1) as u16;
+
+                let mut buffer = buffer.clone();
+                // inject bad total_len
+                let bad_total_len_be = bad_total_len.to_be_bytes();
+                buffer[2] = bad_total_len_be[0];
+                buffer[3] = bad_total_len_be[1];
+
+                let actual = IpHeaders::from_ipv4_slice_lax(&buffer[..]).unwrap();
+
+                let (v4_header, v4_exts) = header.ipv4().unwrap();
+                let expected_headers = IpHeaders::Ipv4(
+                    {
+                        let mut expected_v4 = v4_header.clone();
+                        expected_v4.total_len = bad_total_len;
+                        expected_v4
+                    },
+                    v4_exts.clone()
+                );
+                assert_eq!(&expected_headers, &actual.0);
+                assert_eq!(
+                    actual.1,
+                    LaxIpPayloadSlice{
+                        incomplete: false,
+                        ip_number: header.next_header().unwrap(),
+                        fragmented: header.is_fragmenting_payload(),
+                        len_source: LenSource::Slice,
+                        payload: &buffer[v4_header.header_len() + v4_exts.header_len()..],
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_ipv6_slice(
+            v6 in ipv6_any(),
+            v6_exts in ipv6_extensions_any(),
+        ) {
+            let payload = [1,2,3,4];
+            let header = combine_v6(&v6, &v6_exts, &payload);
+            let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1);
+            header.write(&mut buffer).unwrap();
+            buffer.extend_from_slice(&payload);
+            buffer.push(1); // add some value to check the return slice
+
+            // len error
+            {
+                let actual = IpHeaders::from_ipv6_slice(&buffer).unwrap();
+                assert_eq!(&actual.0, &header);
+                assert_eq!(
+                    actual.1,
+                    IpPayloadSlice{
+                        ip_number: header.next_header().unwrap(),
+                        fragmented: header.is_fragmenting_payload(),
+                        len_source: LenSource::Ipv6HeaderPayloadLen,
+                        payload: &payload
+                    }
+                );
+            }
+
+            // read error header
+            IpHeaders::from_ipv6_slice(&buffer[..1]).unwrap_err();
+
+            // read error ipv4 extensions
+            if v6_exts.header_len() > 0 {
+                IpHeaders::from_ipv6_slice(&buffer[..Ipv6Header::LEN + 1]).unwrap_err();
+            }
+
+            // len error (with payload len zero)
+            if v6_exts.header_len() > 0 {
+                let mut buffer = buffer.clone();
+
+                // inject zero as payload len
+                buffer[4] = 0;
+                buffer[5] = 0;
+
+                assert!(
+                    IpHeaders::from_ipv6_slice(
+                        &buffer[..buffer.len() - payload.len() - 2]
+                    ).is_err()
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_ipv6_slice_lax(
+            v6 in ipv6_any(),
+            v6_exts in ipv6_extensions_any(),
+            bad_version in 0..0xfu8
+        ) {
+            use err::ipv6::{HeaderError::*, HeaderSliceError::*};
+
+            let payload = [1,2,3,4];
+
+            // empty error
+            assert_eq!(
+                IpHeaders::from_ipv6_slice_lax(&[]),
+                Err(Len(err::LenError {
+                    required_len: Ipv6Header::LEN,
+                    len: 0,
+                    len_source: LenSource::Slice,
+                    layer: err::Layer::Ipv6Header,
+                    layer_start_offset: 0,
+                }))
+            );
+
+            // setup buffer with a valid packet
+            let header = combine_v6(&v6, &v6_exts, &payload);
+            let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1);
+            header.write(&mut buffer).unwrap();
+            buffer.extend_from_slice(&payload);
+            buffer.push(1); // add some value to check the return slice
+
+            // unknown version
+            if bad_version != 6 {
+                let mut bad_vers_buffer = buffer.clone();
+                bad_vers_buffer[0] = (bad_vers_buffer[0] & 0xf) | (bad_version << 4);
+                assert_eq!(
+                    IpHeaders::from_ipv6_slice_lax(&bad_vers_buffer),
+                    Err(Content(UnexpectedVersion { version_number: bad_version }))
+                );
+            }
+
+            // normal read
+            {
+                let actual = IpHeaders::from_ipv6_slice_lax(&buffer).unwrap();
+                assert_eq!(&actual.0, &header);
+                assert_eq!(
+                    actual.1,
+                    LaxIpPayloadSlice{
+                        incomplete: false,
+                        ip_number: header.next_header().unwrap(),
+                        fragmented: header.is_fragmenting_payload(),
+                        len_source: LenSource::Ipv6HeaderPayloadLen,
+                        payload: &payload
+                    }
+                );
+            }
+
+            // smaller then header
+            for len in 1..Ipv6Header::LEN {
+                assert_eq!(
+                    IpHeaders::from_ipv6_slice_lax(&buffer[..len]),
+                    Err(Len(err::LenError {
+                        required_len: Ipv6Header::LEN,
+                        len,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::Ipv6Header,
+                        layer_start_offset: 0,
+                    }))
+                );
+            }
+
+            // extension len error
+            if v6_exts.header_len() > 0 {
+                let (_, _, err) = IpHeaders::from_ipv6_slice_lax(&buffer[..v6.header_len() + 1]).unwrap();
+                assert!(err.is_some());
+            }
+
+            // extension content error
+            if v6_exts.auth.is_some() {
+                use err::ip_auth::HeaderError::ZeroPayloadLen;
+                use err::ipv6_exts::{HeaderSliceError::Content, HeaderError::IpAuth};
+
+                // introduce a auth header zero payload error
+                let mut errored_buffer = buffer.clone();
+                let auth_offset = v6.header_len() +
+                    v6_exts.hop_by_hop_options.as_ref().map(|h| h.header_len()).unwrap_or(0) +
+                    v6_exts.destination_options.as_ref().map(|h| h.header_len()).unwrap_or(0) +
+                    v6_exts.routing.as_ref().map(|h| h.routing.header_len()).unwrap_or(0) +
+                    // routing.final_destination_options skiped, as after auth
+                    v6_exts.fragment.as_ref().map(|h| h.header_len()).unwrap_or(0);
+
+                // inject length zero into auth header (not valid, will
+                // trigger a content error)
+                errored_buffer[auth_offset + 1] = 0;
+                let (_, _, err) = IpHeaders::from_ipv6_slice_lax(&errored_buffer).unwrap();
+                assert_eq!(err, Some((Content(IpAuth(ZeroPayloadLen)), Layer::IpAuthHeader)));
+            }
+
+            // slice smaller then payload len
+            for len in (v6.header_len()+v6_exts.header_len())..buffer.len() - 1 {
+                let actual = IpHeaders::from_ipv6_slice_lax(&buffer[..len]).unwrap();
+                assert_eq!(&actual.0, &header);
+                assert_eq!(
+                    actual.1,
+                    LaxIpPayloadSlice{
+                        incomplete: true,
+                        ip_number: header.next_header().unwrap(),
+                        fragmented: header.is_fragmenting_payload(),
+                        len_source: LenSource::Slice,
+                        payload: &payload[..len - v6.header_len() - v6_exts.header_len()]
+                    }
+                );
+            }
+
+            // payload len zero (fallback to slice len)
+            {
+                let mut buffer = buffer.clone();
+                // inject zero as payload len
+                buffer[4] = 0;
+                buffer[5] = 0;
+
+                let actual = IpHeaders::from_ipv6_slice_lax(&buffer[..]).unwrap();
+
+                let (v6_header, v6_exts) = header.ipv6().unwrap();
+                let expected_headers = IpHeaders::Ipv6(
+                    {
+                        let mut expected_v6 = v6_header.clone();
+                        expected_v6.payload_length = 0;
+                        expected_v6
+                    },
+                    v6_exts.clone()
+                );
+                assert_eq!(&expected_headers, &actual.0);
+                assert_eq!(
+                    actual.1,
+                    LaxIpPayloadSlice{
+                        incomplete: false,
+                        ip_number: header.next_header().unwrap(),
+                        fragmented: header.is_fragmenting_payload(),
+                        len_source: LenSource::Slice,
+                        payload: &buffer[v6_header.header_len() + v6_exts.header_len()..],
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read(
+            v4 in ipv4_any(),
+            v4_exts in ipv4_extensions_any(),
+            bad_ihl in 0u8..5u8,
+            v6 in ipv6_any(),
+            v6_exts in ipv6_extensions_any(),
+        ) {
+            use err::ip::{HeadersError::*, HeaderError::*};
+
+            // no data error
+            {
+                let mut cursor = Cursor::new(&[]);
+                assert!(
+                    IpHeaders::read(&mut cursor)
+                    .unwrap_err()
+                    .io()
+                    .is_some()
+                );
+            }
+            // version error
+            {
+                let mut cursor = Cursor::new(&[0xf << 4]);
+                assert_eq!(
+                    IpHeaders::read(&mut cursor).unwrap_err().content().unwrap(),
+                    Ip(UnsupportedIpVersion {
+                        version_number: 0xf
+                    })
+                );
+            }
+            // v4
+            {
+                let header = combine_v4(&v4, &v4_exts, &[]);
+                let mut buffer = Vec::with_capacity(header.header_len());
+                header.write(&mut buffer).unwrap();
+
+                // read
+                {
+                    let mut cursor = Cursor::new(&buffer[..]);
+                    let actual = IpHeaders::read(&mut cursor).unwrap();
+                    assert_eq!(actual.0, header);
+                    assert_eq!(actual.1, header.next_header().unwrap());
+                }
+
+                // read error ihl smaller then header
+                {
+                    let mut buffer = buffer.clone();
+                    // inject bad ihl
+                    buffer[0] = (buffer[0] & 0b1111_0000) | bad_ihl;
+                    let mut cursor = Cursor::new(&buffer[..]);
+                    assert_eq!(
+                        IpHeaders::read(&mut cursor)
+                        .unwrap_err()
+                        .content()
+                        .unwrap(),
+                        Ip(Ipv4HeaderLengthSmallerThanHeader{
+                            ihl: bad_ihl
+                        })
+                    );
+                }
+
+                // total length smaller the header
+                {
+                    let bad_total_len = (v4.header_len() - 1) as u16;
+
+                    let mut buffer = buffer.clone();
+                    // inject bad total_len
+                    let bad_total_len_be = bad_total_len.to_be_bytes();
+                    buffer[2] = bad_total_len_be[0];
+                    buffer[3] = bad_total_len_be[1];
+                    let mut cursor = Cursor::new(&buffer[..]);
+                    assert_eq!(
+                        IpHeaders::read(&mut cursor)
+                        .unwrap_err()
+                        .len()
+                        .unwrap(),
+                        LenError{
+                            required_len: v4.header_len(),
+                            len: bad_total_len as usize,
+                            len_source: LenSource::Ipv4HeaderTotalLen,
+                            layer: Layer::Ipv4Packet,
+                            layer_start_offset: 0,
+                        }
+                    );
+                }
+
+                // read len error ipv4
+                {
+                    let mut cursor = Cursor::new(&buffer[..1]);
+                    assert!(
+                        IpHeaders::read(&mut cursor)
+                        .unwrap_err()
+                        .io()
+                        .is_some()
+                    );
+                }
+
+                // read error ipv4 extensions
+                if v4_exts.header_len() > 0 {
+                    let mut cursor = Cursor::new(&buffer[..v4.header_len() + 1]);
+                    IpHeaders::read(&mut cursor).unwrap_err();
+                }
+
+                // len error in extensions
+                if v4_exts.auth.is_some() {
+                    let bad_total_len = (buffer.len() - 1) as u16;
+
+                    let mut buffer = buffer.clone();
+                    // inject bad total_len
+                    let bad_total_len_be = bad_total_len.to_be_bytes();
+                    buffer[2] = bad_total_len_be[0];
+                    buffer[3] = bad_total_len_be[1];
+                    let mut cursor = Cursor::new(&buffer[..]);
+                    assert_eq!(
+                        IpHeaders::read(&mut cursor)
+                        .unwrap_err()
+                        .len()
+                        .unwrap(),
+                        LenError{
+                            required_len: buffer.len() - v4.header_len(),
+                            len: bad_total_len as usize - v4.header_len(),
+                            len_source: LenSource::Ipv4HeaderTotalLen,
+                            layer: Layer::IpAuthHeader,
+                            layer_start_offset: v4.header_len(),
+                        }
+                    );
+                }
+
+                // extension content error
+                if v4_exts.auth.is_some() {
+                    let mut buffer = buffer.clone();
+                    // inject zero as header len
+                    buffer[v4.header_len() + 1] = 0;
+                    let mut cursor = Cursor::new(&buffer[..]);
+                    assert_eq!(
+                        IpHeaders::read(&mut cursor)
+                        .unwrap_err()
+                        .content()
+                        .unwrap(),
+                        HeadersError::Ipv4Ext(
+                            err::ip_auth::HeaderError::ZeroPayloadLen
+                        )
+                    );
+                }
+            }
+
+            // v6
+            {
+                let header = combine_v6(&v6, &v6_exts, &[]);
+                let mut buffer = Vec::with_capacity(header.header_len());
+                header.write(&mut buffer).unwrap();
+
+                // ok case
+                {
+                    let mut cursor = Cursor::new(&buffer[..]);
+                    let actual = IpHeaders::read(&mut cursor).unwrap();
+                    assert_eq!(actual.0, header);
+                    assert_eq!(actual.1, header.next_header().unwrap());
+                }
+
+                // io error in v6 header section
+                {
+                    let mut cursor = Cursor::new(&buffer[..1]);
+                    assert!(
+                        IpHeaders::read(&mut cursor).unwrap_err().io().is_some()
+                    );
+                }
+
+                // io error ipv6 extensions
+                if v6_exts.header_len() > 0 {
+                    let mut cursor = Cursor::new(&buffer[..Ipv6Header::LEN + 1]);
+                    assert!(
+                        IpHeaders::read(&mut cursor).unwrap_err().io().is_some()
+                    );
+                }
+
+                // len error in ipv6 extensions
+                if v6_exts.header_len() > 0 {
+                    // inject an invalid length
+                    let mut buffer = buffer.clone();
+                    let bad_payload_len = (buffer.len() - header.header_len()) as u16;
+                    let bad_payload_len_be = bad_payload_len.to_be_bytes();
+                    buffer[4] = bad_payload_len_be[0];
+                    buffer[5] = bad_payload_len_be[1];
+                    // expect a length error
+                    let mut cursor = Cursor::new(&buffer[..]);
+                    assert!(
+                        IpHeaders::read(&mut cursor).unwrap_err().len().is_some()
+                    );
+                }
+
+                // extension content error
+                if let Some(auth) = v6_exts.auth.as_ref() {
+                    // only do it if auth is the last header
+                    if v6_exts.routing.is_none() {
+                        // inject zero as header len
+                        let mut buffer = buffer.clone();
+                        let auth_offset = buffer.len() - auth.header_len();
+                        buffer[auth_offset + 1] = 0;
+                        let mut cursor = Cursor::new(&buffer[..]);
+                        assert_eq!(
+                            IpHeaders::read(&mut cursor)
+                            .unwrap_err()
+                            .content()
+                            .unwrap(),
+                            HeadersError::Ipv6Ext(err::ipv6_exts::HeaderError::IpAuth(
+                                err::ip_auth::HeaderError::ZeroPayloadLen
+                            ))
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write(
+            v4 in ipv4_any(),
+            v4_exts in ipv4_extensions_any(),
+            v6 in ipv6_any(),
+            v6_exts in ipv6_extensions_any(),
+        ) {
+            // v4
+            {
+                let header = combine_v4(&v4, &v4_exts, &[]);
+                let mut buffer = Vec::with_capacity(header.header_len());
+                header.write(&mut buffer).unwrap();
+
+                let actual = IpHeaders::from_slice(&buffer).unwrap().0;
+                assert_eq!(header, actual);
+
+                // write error v4 header
+                {
+                    let mut buffer = [0u8;1];
+                    let mut cursor = Cursor::new(&mut buffer[..]);
+                    assert!(
+                        header.write(&mut cursor)
+                        .unwrap_err()
+                        .io()
+                        .is_some()
+                    );
+                }
+
+                // write io error v4 extension headers
+                if v4_exts.header_len() > 0 {
+                    let mut buffer = [0u8;Ipv4Header::MAX_LEN + 1];
+                    let mut cursor = Cursor::new(&mut buffer[..v4.header_len() + 1]);
+                    assert!(
+                        header.write(&mut cursor)
+                        .unwrap_err()
+                        .io()
+                        .is_some()
+                    );
+                }
+
+                // write content error v4 extension headers
+                if v4_exts.header_len() > 0 {
+                    // cause a missing reference error
+                    let header = IpHeaders::Ipv4(
+                        {
+                            let mut v4 = v4.clone();
+                            // skips extension header
+                            v4.protocol = ip_number::UDP;
+                            v4.total_len = (v4.header_len() + v4_exts.header_len()) as u16;
+                            v4.header_checksum = v4.calc_header_checksum();
+                            v4
+                        },
+                        v4_exts.clone(),
+                    );
+                    let mut buffer = [0u8;Ipv4Header::MAX_LEN + IpAuthHeader::MAX_LEN];
+                    let mut cursor = Cursor::new(&mut buffer[..]);
+                    assert!(header.write(&mut cursor).is_err());
+                }
+            }
+
+            // v6
+            {
+                let header = combine_v6(&v6, &v6_exts, &[]);
+
+                // normal write
+                let mut buffer = Vec::with_capacity(header.header_len());
+                header.write(&mut buffer).unwrap();
+
+                let actual = IpHeaders::from_slice(&buffer).unwrap().0;
+                assert_eq!(header, actual);
+
+                // write error v6 header
+                {
+                    let mut buffer = [0u8;1];
+                    let mut cursor = Cursor::new(&mut buffer[..]);
+                    assert!(
+                        header.write(&mut cursor)
+                        .unwrap_err()
+                        .io()
+                        .is_some()
+                    );
+                }
+
+                // write error v6 extension headers
+                if v6_exts.header_len() > 0 {
+                    let mut buffer = [0u8;Ipv6Header::LEN + 1];
+                    let mut cursor = Cursor::new(&mut buffer[..]);
+                    assert!(
+                        header.write(&mut cursor)
+                        .unwrap_err()
+                        .io()
+                        .is_some()
+                    );
+                }
+                // write content error v4 extension headers
+                if v6_exts.header_len() > 0 {
+                    // cause a missing reference error
+                    let header = IpHeaders::Ipv6(
+                        {
+                            let mut v6 = v6.clone();
+                            // skips extension header
+                            v6.next_header = ip_number::UDP;
+                            v6.payload_length = v6_exts.header_len() as u16;
+                            v6
+                        },
+                        v6_exts.clone(),
+                    );
+                    let mut buffer = [0u8;Ipv4Header::MAX_LEN + IpAuthHeader::MAX_LEN];
+                    let mut cursor = Cursor::new(&mut buffer[..]);
+                    assert!(header.write(&mut cursor).is_err());
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header_len(
+            v4 in ipv4_any(),
+            v4_exts in ipv4_extensions_any(),
+            v6 in ipv6_any(),
+            v6_exts in ipv6_extensions_any(),
+        ) {
+            assert_eq!(
+                v4.header_len() + v4_exts.header_len(),
+                IpHeaders::Ipv4(v4, v4_exts).header_len()
+            );
+            assert_eq!(
+                Ipv6Header::LEN + v6_exts.header_len(),
+                IpHeaders::Ipv6(v6, v6_exts).header_len()
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn next_header(
+            v4 in ipv4_any(),
+            v4_exts in ipv4_extensions_any(),
+            v6 in ipv6_any(),
+            v6_exts in ipv6_extensions_any(),
+            post_header in ip_number_any()
+                .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(),
+                    |v| !EXTENSION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x)
+                )
+        ) {
+            {
+                let mut header = v4.clone();
+                let mut exts = v4_exts.clone();
+                header.protocol = exts.set_next_headers(post_header);
+                assert_eq!(
+                    Ok(post_header),
+                    IpHeaders::Ipv4(header, exts).next_header()
+                );
+            }
+            {
+                let mut header = v6.clone();
+                let mut exts = v6_exts.clone();
+                header.next_header = exts.set_next_headers(post_header);
+                assert_eq!(
+                    Ok(post_header),
+                    IpHeaders::Ipv6(header, exts).next_header()
+                );
+            }
+        }
+    }
+
+    // TODO set_next_headers
+
+    proptest! {
+        #[test]
+        fn set_payload_len(
+            v4 in ipv4_any(),
+            v4_exts in ipv4_extensions_any(),
+            v6 in ipv6_any(),
+            v6_exts in ipv6_extensions_any(),
+            payload_len in 0usize..10
+        ) {
+            // ipv4 (with valid payload length)
+            {
+                let mut actual = IpHeaders::Ipv4(
+                    v4.clone(),
+                    v4_exts.clone()
+                );
+                actual.set_payload_len(payload_len).unwrap();
+
+                assert_eq!(
+                    actual,
+                    IpHeaders::Ipv4(
+                        {
+                            let mut re = v4.clone();
+                            re.set_payload_len(v4_exts.header_len() + payload_len).unwrap();
+                            re
+                        },
+                        v4_exts.clone()
+                    )
+                );
+            }
+            // ipv6 (with valid payload length)
+            {
+                let mut actual = IpHeaders::Ipv6(
+                    v6.clone(),
+                    v6_exts.clone()
+                );
+                actual.set_payload_len(payload_len).unwrap();
+
+                assert_eq!(
+                    actual,
+                    IpHeaders::Ipv6(
+                        {
+                            let mut re = v6.clone();
+                            re.set_payload_length(v6_exts.header_len() + payload_len).unwrap();
+                            re
+                        },
+                        v6_exts.clone()
+                    )
+                );
+            }
+
+            // v4 (with invalid size)
+            {
+                let mut actual = IpHeaders::Ipv4(
+                    v4.clone(),
+                    v4_exts.clone()
+                );
+                assert!(actual.set_payload_len(usize::MAX).is_err());
+            }
+
+            // v6 (with invalid size)
+            {
+                let mut actual = IpHeaders::Ipv6(
+                    v6.clone(),
+                    v6_exts.clone()
+                );
+                assert!(actual.set_payload_len(usize::MAX).is_err());
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn is_fragmenting_payload(
+            v4 in ipv4_any(),
+            v4_exts in ipv4_extensions_any(),
+            v6 in ipv6_any(),
+            v6_exts in ipv6_extensions_any()
+        ) {
+            // ipv4
+            assert_eq!(
+                v4.is_fragmenting_payload(),
+                IpHeaders::Ipv4(v4.clone(), v4_exts.clone()).is_fragmenting_payload()
+            );
+
+            // ipv6
+            assert_eq!(
+                v6_exts.is_fragmenting_payload(),
+                IpHeaders::Ipv6(v6.clone(), v6_exts.clone()).is_fragmenting_payload()
+            );
+        }
+    }
+}
diff --git a/src/net/ip_number_impl.rs b/src/net/ip_number_impl.rs
new file mode 100644
index 0000000..2550273
--- /dev/null
+++ b/src/net/ip_number_impl.rs
@@ -0,0 +1,1894 @@
+/// This type has been deprecated please use [IpNumber] instead.
+///
+/// IPv6 headers have a field called `traffic_class` which has nothing
+/// to do this enum. This unlucky coincedence got even the developer
+/// of this library confused enough to write that the next header number
+/// should be written into the `traffic_class` field instead of the
+/// `next_header` field.
+///
+/// To avoid such confusions in the future the enum has been renamed
+/// to [IpNumber], which also closer to the name
+/// "Assigned Internet Protocol Numbers" used on iana.org .
+#[deprecated(since = "0.10.1", note = "Please use the type IpNumber instead")]
+pub type IpTrafficClass = IpNumber;
+
+/// Identifiers for the next_header field in ipv6 headers and protocol field in ipv4 headers.
+///
+/// You can access the underlying `u8` value by using `.0` and any `u8`
+/// can be converted to an `IpNumber`:
+///
+/// ```
+/// use etherparse::IpNumber;
+///
+/// assert_eq!(IpNumber::TCP.0, 6);
+/// assert_eq!(IpNumber::TCP, IpNumber(6));
+///
+/// // convert to IpNumber using the from & into trait
+/// let ip_num: IpNumber = 6.into();
+/// assert_eq!(IpNumber::TCP, ip_num);
+///
+/// // convert to u8 using the from & into trait
+/// let num: u8 = IpNumber::TCP.into();
+/// assert_eq!(6, num);
+/// ```
+///
+/// The constants are also defined in the `ip_number` module so they can
+/// be used without the need to write `IpNumber::` in front of them:
+///
+/// ```
+/// use etherparse::{ip_number::TCP, IpNumber};
+///
+/// assert_eq!(TCP, IpNumber::TCP);
+/// ```
+///
+/// The list original values were copied from
+/// <https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml>
+#[derive(PartialEq, Eq, Clone, Copy, Hash, Ord, PartialOrd)]
+pub struct IpNumber(pub u8);
+
+impl IpNumber {
+    /// IPv6 Hop-by-Hop Option \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\]
+    pub const IPV6_HEADER_HOP_BY_HOP: IpNumber = Self(0);
+    /// Internet Control Message \[[RFC792](https://datatracker.ietf.org/doc/html/rfc792)\]
+    pub const ICMP: IpNumber = Self(1);
+    /// Internet Group Management \[[RFC1112](https://datatracker.ietf.org/doc/html/rfc1112)\]
+    pub const IGMP: IpNumber = Self(2);
+    /// Gateway-to-Gateway \[[RFC823](https://datatracker.ietf.org/doc/html/rfc823)\]
+    pub const GGP: IpNumber = Self(3);
+    /// IPv4 encapsulation \[[RFC2003](https://datatracker.ietf.org/doc/html/rfc2003)\]
+    pub const IPV4: IpNumber = Self(4);
+    /// Stream \[[RFC1190](https://datatracker.ietf.org/doc/html/rfc1190)\] \[[RFC1819](https://datatracker.ietf.org/doc/html/rfc1819)\]
+    pub const STREAM: IpNumber = Self(5);
+    /// Transmission Control \[[RFC793](https://datatracker.ietf.org/doc/html/rfc793)\]
+    pub const TCP: IpNumber = Self(6);
+    /// CBT \[Tony_Ballardie\]
+    pub const CBT: IpNumber = Self(7);
+    /// Exterior Gateway Protocol \[[RFC888](https://datatracker.ietf.org/doc/html/rfc888)\] \[David_Mills\]
+    pub const EGP: IpNumber = Self(8);
+    /// any private interior gateway (used by Cisco for their IGRP) \[Internet_Assigned_Numbers_Authority\]
+    pub const IGP: IpNumber = Self(9);
+    /// BBN RCC Monitoring \[Steve_Chipman\]
+    pub const BBN_RCC_MON: IpNumber = Self(10);
+    /// Network Voice Protocol \[[RFC741](https://datatracker.ietf.org/doc/html/rfc741)\]\[Steve_Casner\]
+    pub const NVP_II: IpNumber = Self(11);
+    /// PUP
+    pub const PUP: IpNumber = Self(12);
+    /// ARGUS (deprecated) \[Robert_W_Scheifler\]
+    pub const ARGUS: IpNumber = Self(13);
+    /// EMCON \[mystery contact\]
+    pub const EMCON: IpNumber = Self(14);
+    /// Cross Net Debugger \[Haverty, J., "XNET Formats for Internet Protocol Version 4", IEN 158, October 1980.\]\[Jack_Haverty\]
+    pub const XNET: IpNumber = Self(15);
+    /// Chaos \[J_Noel_Chiappa\]
+    pub const CHAOS: IpNumber = Self(16);
+    /// User Datagram \[[RFC768](https://datatracker.ietf.org/doc/html/rfc768)\]\[Jon_Postel\]
+    pub const UDP: IpNumber = Self(17);
+    /// Multiplexing \[Cohen, D. and J. Postel, "Multiplexing Protocol", IEN 90, USC/Information Sciences Institute, May 1979.\]\[Jon_Postel\]
+    pub const MUX: IpNumber = Self(18);
+    /// DCN Measurement Subsystems \[David_Mills\]
+    pub const DCN_MEAS: IpNumber = Self(19);
+    /// Host Monitoring \[[RFC869](https://datatracker.ietf.org/doc/html/rfc869)\]\[Bob_Hinden\]
+    pub const HMP: IpNumber = Self(20);
+    /// Packet Radio Measurement \[Zaw_Sing_Su\]
+    pub const PRM: IpNumber = Self(21);
+    /// XEROX NS IDP
+    pub const XNS_IDP: IpNumber = Self(22);
+    /// Trunk-1 \[Barry_Boehm\]
+    pub const TRUNK1: IpNumber = Self(23);
+    /// Trunk-2 \[Barry_Boehm\]
+    pub const TRUNK2: IpNumber = Self(24);
+    /// Leaf-1 \[Barry_Boehm\]
+    pub const LEAF1: IpNumber = Self(25);
+    /// Leaf-2 \[Barry_Boehm\]
+    pub const LEAF2: IpNumber = Self(26);
+    /// Reliable Data Protocol \[[RFC908](https://datatracker.ietf.org/doc/html/rfc908)\] \[Bob_Hinden\]
+    pub const RDP: IpNumber = Self(27);
+    /// Internet Reliable Transaction \[[RFC938](https://datatracker.ietf.org/doc/html/rfc938)\] \[Trudy_Miller\]
+    pub const IRTP: IpNumber = Self(28);
+    /// ISO Transport Protocol Class 4 \[[RFC905](https://datatracker.ietf.org/doc/html/rfc905)\] \[mystery contact\]
+    pub const ISO_TP4: IpNumber = Self(29);
+    /// Bulk Data Transfer Protocol \[[RFC969](https://datatracker.ietf.org/doc/html/rfc969)\] \[David_Clark\]
+    pub const NET_BLT: IpNumber = Self(30);
+    /// MFE Network Services Protocol \[Shuttleworth, B., "A Documentary of MFENet, a National Computer Network", UCRL-52317, Lawrence Livermore Labs, Livermore, California, June 1977.\] \[Barry_Howard\]
+    pub const MFE_NSP: IpNumber = Self(31);
+    /// MERIT Internodal Protocol \[Hans_Werner_Braun\]
+    pub const MERIT_INP: IpNumber = Self(32);
+    /// Datagram Congestion Control Protocol \[[RFC4340](https://datatracker.ietf.org/doc/html/rfc4340)\]
+    pub const DCCP: IpNumber = Self(33);
+    /// Third Party Connect Protocol \[Stuart_A_Friedberg\]
+    pub const THIRD_PARTY_CONNECT_PROTOCOL: IpNumber = Self(34);
+    /// Inter-Domain Policy Routing Protocol \[Martha_Steenstrup\]
+    pub const IDPR: IpNumber = Self(35);
+    /// XTP \[Greg_Chesson\]
+    pub const XTP: IpNumber = Self(36);
+    /// Datagram Delivery Protocol \[Wesley_Craig\]
+    pub const DDP: IpNumber = Self(37);
+    /// IDPR Control Message Transport Proto \[Martha_Steenstrup\]
+    pub const IDPR_CMTP: IpNumber = Self(38);
+    /// TP++ Transport Protocol \[Dirk_Fromhein\]
+    pub const TP_PLUS_PLUS: IpNumber = Self(39);
+    /// IL Transport Protocol \[Dave_Presotto\]
+    pub const IL: IpNumber = Self(40);
+    /// IPv6 encapsulation \[[RFC2473](https://datatracker.ietf.org/doc/html/rfc2473)\]
+    pub const IPV6: IpNumber = Self(41);
+    /// Source Demand Routing Protocol \[Deborah_Estrin\]
+    pub const SDRP: IpNumber = Self(42);
+    /// Routing Header for IPv6 \[Steve_Deering\]
+    pub const IPV6_ROUTE_HEADER: IpNumber = Self(43);
+    /// Fragment Header for IPv6 \[Steve_Deering\]
+    pub const IPV6_FRAGMENTATION_HEADER: IpNumber = Self(44);
+    /// Inter-Domain Routing Protocol \[Sue_Hares\]
+    pub const IDRP: IpNumber = Self(45);
+    /// Reservation Protocol \[[RFC2205](https://datatracker.ietf.org/doc/html/rfc2205)\]\[[RFC3209](https://datatracker.ietf.org/doc/html/rfc3209)\]\[Bob_Braden\]
+    pub const RSVP: IpNumber = Self(46);
+    /// Generic Routing Encapsulation \[[RFC2784](https://datatracker.ietf.org/doc/html/rfc2784)\]\[Tony_Li\]
+    pub const GRE: IpNumber = Self(47);
+    /// Dynamic Source Routing Protocol \[[RFC4728](https://datatracker.ietf.org/doc/html/rfc4728)\]
+    pub const DSR: IpNumber = Self(48);
+    /// BNA \[Gary Salamon\]
+    pub const BNA: IpNumber = Self(49);
+    /// Encapsulating Security Payload \[[RFC4303](https://datatracker.ietf.org/doc/html/rfc4303)\]
+    pub const ENCAPSULATING_SECURITY_PAYLOAD: IpNumber = Self(50);
+    /// Authentication Header \[[RFC4302](https://datatracker.ietf.org/doc/html/rfc4302)\]
+    pub const AUTHENTICATION_HEADER: IpNumber = Self(51);
+    /// Integrated Net Layer Security  TUBA \[K_Robert_Glenn\]
+    pub const INLSP: IpNumber = Self(52);
+    /// IP with Encryption (deprecated) \[John_Ioannidis\]
+    pub const SWIPE: IpNumber = Self(53);
+    /// NBMA Address Resolution Protocol \[[RFC1735](https://datatracker.ietf.org/doc/html/rfc1735)\]
+    pub const NARP: IpNumber = Self(54);
+    /// IP Mobility \[Charlie_Perkins\]
+    pub const MOBILE: IpNumber = Self(55);
+    /// Transport Layer Security Protocol using Kryptonet key management \[Christer_Oberg\]
+    pub const TLSP: IpNumber = Self(56);
+    /// SKIP \[Tom_Markson\]
+    pub const SKIP: IpNumber = Self(57);
+    /// ICMP for IPv6 \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\]
+    pub const IPV6_ICMP: IpNumber = Self(58);
+    /// No Next Header for IPv6 \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\]
+    pub const IPV6_NO_NEXT_HEADER: IpNumber = Self(59);
+    /// Destination Options for IPv6 \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\]
+    pub const IPV6_DESTINATION_OPTIONS: IpNumber = Self(60);
+    /// any host internal protocol \[Internet_Assigned_Numbers_Authority\]
+    pub const ANY_HOST_INTERNAL_PROTOCOL: IpNumber = Self(61);
+    /// CFTP \[Forsdick, H., "CFTP", Network Message, Bolt Beranek and Newman, January 1982.\]\[Harry_Forsdick\]
+    pub const CFTP: IpNumber = Self(62);
+    /// any local network \[Internet_Assigned_Numbers_Authority\]
+    pub const ANY_LOCAL_NETWORK: IpNumber = Self(63);
+    /// SATNET and Backroom EXPAK \[Steven_Blumenthal\]
+    pub const SAT_EXPAK: IpNumber = Self(64);
+    /// Kryptolan \[Paul Liu\]
+    pub const KRYTOLAN: IpNumber = Self(65);
+    /// MIT Remote Virtual Disk Protocol \[Michael_Greenwald\]
+    pub const RVD: IpNumber = Self(66);
+    /// Internet Pluribus Packet Core \[Steven_Blumenthal\]
+    pub const IPPC: IpNumber = Self(67);
+    /// any distributed file system \[Internet_Assigned_Numbers_Authority\]
+    pub const ANY_DISTRIBUTED_FILE_SYSTEM: IpNumber = Self(68);
+    /// SATNET Monitoring \[Steven_Blumenthal\]
+    pub const SAT_MON: IpNumber = Self(69);
+    /// VISA Protocol \[Gene_Tsudik\]
+    pub const VISA: IpNumber = Self(70);
+    /// Internet Packet Core Utility \[Steven_Blumenthal\]
+    pub const IPCV: IpNumber = Self(71);
+    /// Computer Protocol Network Executive \[David Mittnacht\]
+    pub const CPNX: IpNumber = Self(72);
+    /// Computer Protocol Heart Beat \[David Mittnacht\]
+    pub const CPHB: IpNumber = Self(73);
+    /// Wang Span Network \[Victor Dafoulas\]
+    pub const WSN: IpNumber = Self(74);
+    /// Packet Video Protocol \[Steve_Casner\]
+    pub const PVP: IpNumber = Self(75);
+    /// Backroom SATNET Monitoring \[Steven_Blumenthal\]
+    pub const BR_SAT_MON: IpNumber = Self(76);
+    /// SUN ND PROTOCOL-Temporary \[William_Melohn\]
+    pub const SUN_ND: IpNumber = Self(77);
+    /// WIDEBAND Monitoring \[Steven_Blumenthal\]
+    pub const WB_MON: IpNumber = Self(78);
+    /// WIDEBAND EXPAK \[Steven_Blumenthal\]
+    pub const WB_EXPAK: IpNumber = Self(79);
+    /// ISO Internet Protocol \[Marshall_T_Rose\]
+    pub const ISO_IP: IpNumber = Self(80);
+    /// VMTP \[Dave_Cheriton\]
+    pub const VMTP: IpNumber = Self(81);
+    /// SECURE-VMTP \[Dave_Cheriton\]
+    pub const SECURE_VMTP: IpNumber = Self(82);
+    /// VINES \[Brian Horn\]
+    pub const VINES: IpNumber = Self(83);
+    /// Transaction Transport Protocol or Internet Protocol Traffic Manager \[Jim_Stevens\]
+    pub const TTP_OR_IPTM: IpNumber = Self(84);
+    /// NSFNET-IGP \[Hans_Werner_Braun\]
+    pub const NSFNET_IGP: IpNumber = Self(85);
+    /// Dissimilar Gateway Protocol \[M/A-COM Government Systems, "Dissimilar Gateway Protocol Specification, Draft Version", Contract no. CS901145, November 16, 1987.\]\[Mike_Little\]
+    pub const DGP: IpNumber = Self(86);
+    /// TCF \[Guillermo_A_Loyola\]
+    pub const TCF: IpNumber = Self(87);
+    /// EIGRP \[[RFC7868](https://datatracker.ietf.org/doc/html/rfc7868)\]
+    pub const EIGRP: IpNumber = Self(88);
+    /// OSPFIGP \[[RFC1583](https://datatracker.ietf.org/doc/html/rfc1583)\]\[[RFC2328](https://datatracker.ietf.org/doc/html/rfc2328)\]\[[RFC5340](https://datatracker.ietf.org/doc/html/rfc5340)\]\[John_Moy\]
+    pub const OSPFIGP: IpNumber = Self(89);
+    /// Sprite RPC Protocol \[Welch, B., "The Sprite Remote Procedure Call System", Technical Report, UCB/Computer Science Dept., 86/302, University of California at Berkeley, June 1986.\]\[Bruce Willins\]
+    pub const SPRITE_RPC: IpNumber = Self(90);
+    /// Locus Address Resolution Protocol \[Brian Horn\]
+    pub const LARP: IpNumber = Self(91);
+    /// Multicast Transport Protocol \[Susie_Armstrong\]
+    pub const MTP: IpNumber = Self(92);
+    /// AX.25 Frames \[Brian_Kantor\]
+    pub const AX25: IpNumber = Self(93);
+    /// IP-within-IP Encapsulation Protocol \[John_Ioannidis\]
+    pub const IPIP: IpNumber = Self(94);
+    /// Mobile Internetworking Control Pro. (deprecated) \[John_Ioannidis\]
+    pub const MICP: IpNumber = Self(95);
+    /// Semaphore Communications Sec. Pro. \[Howard_Hart\]
+    pub const SCC_SP: IpNumber = Self(96);
+    /// Ethernet-within-IP Encapsulation \[[RFC3378](https://datatracker.ietf.org/doc/html/rfc3378)\]
+    pub const ETHER_IP: IpNumber = Self(97);
+    /// Encapsulation Header \[[RFC1241](https://datatracker.ietf.org/doc/html/rfc1241)\]\[Robert_Woodburn\]
+    pub const ENCAP: IpNumber = Self(98);
+    /// GMTP \[\[RXB5\]\]
+    pub const GMTP: IpNumber = Self(100);
+    /// Ipsilon Flow Management Protocol \[Bob_Hinden\]\[November 1995, 1997.\]
+    pub const IFMP: IpNumber = Self(101);
+    /// PNNI over IP \[Ross_Callon\]
+    pub const PNNI: IpNumber = Self(102);
+    /// Protocol Independent Multicast \[[RFC7761](https://datatracker.ietf.org/doc/html/rfc7761)\]\[Dino_Farinacci\]
+    pub const PIM: IpNumber = Self(103);
+    /// ARIS \[Nancy_Feldman\]
+    pub const ARIS: IpNumber = Self(104);
+    /// SCPS \[Robert_Durst\]
+    pub const SCPS: IpNumber = Self(105);
+    /// QNX \[Michael_Hunter\]
+    pub const QNX: IpNumber = Self(106);
+    /// Active Networks \[Bob_Braden\]
+    pub const ACTIVE_NETWORKS: IpNumber = Self(107);
+    /// IP Payload Compression Protocol \[[RFC2393](https://datatracker.ietf.org/doc/html/rfc2393)\]
+    pub const IP_COMP: IpNumber = Self(108);
+    /// Sitara Networks Protocol \[Manickam_R_Sridhar\]
+    pub const SITRA_NETWORKS_PROTOCOL: IpNumber = Self(109);
+    /// Compaq Peer Protocol \[Victor_Volpe\]
+    pub const COMPAQ_PEER: IpNumber = Self(110);
+    /// IPX in IP \[CJ_Lee\]
+    pub const IPX_IN_IP: IpNumber = Self(111);
+    /// Virtual Router Redundancy Protocol \[[RFC5798](https://datatracker.ietf.org/doc/html/rfc5798)\]
+    pub const VRRP: IpNumber = Self(112);
+    /// PGM Reliable Transport Protocol \[Tony_Speakman\]
+    pub const PGM: IpNumber = Self(113);
+    /// any 0-hop protocol \[Internet_Assigned_Numbers_Authority\]
+    pub const ANY_ZERO_HOP_PROTOCOL: IpNumber = Self(114);
+    /// Layer Two Tunneling Protocol \[[RFC3931](https://datatracker.ietf.org/doc/html/rfc3931)\]\[Bernard_Aboba\]
+    pub const LAYER2_TUNNELING_PROTOCOL: IpNumber = Self(115);
+    /// D-II Data Exchange (DDX) \[John_Worley\]
+    pub const DDX: IpNumber = Self(116);
+    /// Interactive Agent Transfer Protocol \[John_Murphy\]
+    pub const IATP: IpNumber = Self(117);
+    /// Schedule Transfer Protocol \[Jean_Michel_Pittet\]
+    pub const STP: IpNumber = Self(118);
+    /// SpectraLink Radio Protocol \[Mark_Hamilton\]
+    pub const SRP: IpNumber = Self(119);
+    /// UTI \[Peter_Lothberg\]
+    pub const UTI: IpNumber = Self(120);
+    /// Simple Message Protocol \[Leif_Ekblad\]
+    pub const SIMPLE_MESSAGE_PROTOCOL: IpNumber = Self(121);
+    /// Simple Multicast Protocol (deprecated) \[Jon_Crowcroft\]\[draft-perlman-simple-multicast\]
+    pub const SM: IpNumber = Self(122);
+    /// Performance Transparency Protocol \[Michael_Welzl\]
+    pub const PTP: IpNumber = Self(123);
+    /// ISIS over IPv4 \[Tony_Przygienda\]
+    pub const ISIS_OVER_IPV4: IpNumber = Self(124);
+    /// FIRE \[Criag_Partridge\]
+    pub const FIRE: IpNumber = Self(125);
+    /// Combat Radio Transport Protocol \[Robert_Sautter\]
+    pub const CRTP: IpNumber = Self(126);
+    /// Combat Radio User Datagram \[Robert_Sautter\]
+    pub const CRUDP: IpNumber = Self(127);
+    /// SSCOPMCE \[Kurt_Waber\]
+    pub const SSCOPMCE: IpNumber = Self(128);
+    /// IPLT \[\[Hollbach\]\]
+    pub const IPLT: IpNumber = Self(129);
+    /// Secure Packet Shield \[Bill_McIntosh\]
+    pub const SPS: IpNumber = Self(130);
+    /// Private IP Encapsulation within IP \[Bernhard_Petri\]
+    pub const PIPE: IpNumber = Self(131);
+    /// Stream Control Transmission Protocol \[Randall_R_Stewart\]
+    pub const SCTP: IpNumber = Self(132);
+    /// Fibre Channel \[Murali_Rajagopal\]\[[RFC6172](https://datatracker.ietf.org/doc/html/rfc6172)\]
+    pub const FC: IpNumber = Self(133);
+    /// RSVP-E2E-IGNORE \[[RFC3175](https://datatracker.ietf.org/doc/html/rfc3175)\]
+    pub const RSVP_E2E_IGNORE: IpNumber = Self(134);
+    /// MobilityHeader \[[RFC6275](https://datatracker.ietf.org/doc/html/rfc6275)\]
+    pub const MOBILITY_HEADER: IpNumber = Self(135);
+    /// UDPLite \[[RFC3828](https://datatracker.ietf.org/doc/html/rfc3828)\]
+    pub const UDP_LITE: IpNumber = Self(136);
+    /// \[[RFC4023](https://datatracker.ietf.org/doc/html/rfc4023)\]
+    pub const MPLS_IN_IP: IpNumber = Self(137);
+    /// MANET Protocols \[[RFC5498](https://datatracker.ietf.org/doc/html/rfc5498)\]
+    pub const MANET: IpNumber = Self(138);
+    /// Host Identity Protocol \[[RFC7401](https://datatracker.ietf.org/doc/html/rfc7401)\]
+    pub const HIP: IpNumber = Self(139);
+    /// Shim6 Protocol \[[RFC5533](https://datatracker.ietf.org/doc/html/rfc5533)\]
+    pub const SHIM6: IpNumber = Self(140);
+    /// Wrapped Encapsulating Security Payload \[[RFC5840](https://datatracker.ietf.org/doc/html/rfc5840)\]
+    pub const WESP: IpNumber = Self(141);
+    /// Robust Header Compression \[[RFC5858](https://datatracker.ietf.org/doc/html/rfc5858)\]
+    pub const ROHC: IpNumber = Self(142);
+    /// Use for experimentation and testing
+    pub const EXPERIMENTAL_AND_TESTING_0: IpNumber = Self(253);
+    /// Use for experimentation and testing
+    pub const EXPERIMENTAL_AND_TESTING_1: IpNumber = Self(254);
+}
+
+impl IpNumber {
+    /// Returns true if the given number is the internet number of an IPV6 extension header.
+    pub fn is_ipv6_ext_header_value(self) -> bool {
+        use crate::ip_number::*;
+        matches!(
+            self,
+            IPV6_HOP_BY_HOP
+                | IPV6_ROUTE
+                | IPV6_FRAG
+                | ENCAP_SEC
+                | AUTH
+                | IPV6_DEST_OPTIONS
+                | MOBILITY
+                | HIP
+                | SHIM6
+                | EXP0
+                | EXP1
+        )
+    }
+
+    /// Returns the "keyword" string if known. Usually this is the abbreviation of the protocol.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use etherparse::IpNumber;
+    ///
+    /// assert_eq!(IpNumber::UDP.keyword_str(), Some("UDP"));
+    ///
+    /// // Unassigned values return None
+    /// assert_eq!(IpNumber(145).keyword_str(), None);
+    /// ```
+    ///
+    /// # Data Source
+    ///
+    /// The strings were copied from <https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml>
+    /// on 2023-04-11.
+    pub fn keyword_str(self) -> Option<&'static str> {
+        // auto generated from CSV
+        // https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
+        // on 2023-04-11.
+        match self.0 {
+            0 => Some("HOPOPT"),
+            1 => Some("ICMP"),
+            2 => Some("IGMP"),
+            3 => Some("GGP"),
+            4 => Some("IPv4"),
+            5 => Some("ST"),
+            6 => Some("TCP"),
+            7 => Some("CBT"),
+            8 => Some("EGP"),
+            9 => Some("IGP"),
+            10 => Some("BBN-RCC-MON"),
+            11 => Some("NVP-II"),
+            12 => Some("PUP"),
+            13 => Some("ARGUS (deprecated)"),
+            14 => Some("EMCON"),
+            15 => Some("XNET"),
+            16 => Some("CHAOS"),
+            17 => Some("UDP"),
+            18 => Some("MUX"),
+            19 => Some("DCN-MEAS"),
+            20 => Some("HMP"),
+            21 => Some("PRM"),
+            22 => Some("XNS-IDP"),
+            23 => Some("TRUNK-1"),
+            24 => Some("TRUNK-2"),
+            25 => Some("LEAF-1"),
+            26 => Some("LEAF-2"),
+            27 => Some("RDP"),
+            28 => Some("IRTP"),
+            29 => Some("ISO-TP4"),
+            30 => Some("NETBLT"),
+            31 => Some("MFE-NSP"),
+            32 => Some("MERIT-INP"),
+            33 => Some("DCCP"),
+            34 => Some("3PC"),
+            35 => Some("IDPR"),
+            36 => Some("XTP"),
+            37 => Some("DDP"),
+            38 => Some("IDPR-CMTP"),
+            39 => Some("TP++"),
+            40 => Some("IL"),
+            41 => Some("IPv6"),
+            42 => Some("SDRP"),
+            43 => Some("IPv6-Route"),
+            44 => Some("IPv6-Frag"),
+            45 => Some("IDRP"),
+            46 => Some("RSVP"),
+            47 => Some("GRE"),
+            48 => Some("DSR"),
+            49 => Some("BNA"),
+            50 => Some("ESP"),
+            51 => Some("AH"),
+            52 => Some("I-NLSP"),
+            53 => Some("SWIPE (deprecated)"),
+            54 => Some("NARP"),
+            55 => Some("MOBILE"),
+            56 => Some("TLSP"),
+            57 => Some("SKIP"),
+            58 => Some("IPv6-ICMP"),
+            59 => Some("IPv6-NoNxt"),
+            60 => Some("IPv6-Opts"),
+            61 => None,
+            62 => Some("CFTP"),
+            63 => None,
+            64 => Some("SAT-EXPAK"),
+            65 => Some("KRYPTOLAN"),
+            66 => Some("RVD"),
+            67 => Some("IPPC"),
+            68 => None,
+            69 => Some("SAT-MON"),
+            70 => Some("VISA"),
+            71 => Some("IPCV"),
+            72 => Some("CPNX"),
+            73 => Some("CPHB"),
+            74 => Some("WSN"),
+            75 => Some("PVP"),
+            76 => Some("BR-SAT-MON"),
+            77 => Some("SUN-ND"),
+            78 => Some("WB-MON"),
+            79 => Some("WB-EXPAK"),
+            80 => Some("ISO-IP"),
+            81 => Some("VMTP"),
+            82 => Some("SECURE-VMTP"),
+            83 => Some("VINES"),
+            84 => Some("IPTM"),
+            85 => Some("NSFNET-IGP"),
+            86 => Some("DGP"),
+            87 => Some("TCF"),
+            88 => Some("EIGRP"),
+            89 => Some("OSPFIGP"),
+            90 => Some("Sprite-RPC"),
+            91 => Some("LARP"),
+            92 => Some("MTP"),
+            93 => Some("AX.25"),
+            94 => Some("IPIP"),
+            95 => Some("MICP (deprecated)"),
+            96 => Some("SCC-SP"),
+            97 => Some("ETHERIP"),
+            98 => Some("ENCAP"),
+            99 => None,
+            100 => Some("GMTP"),
+            101 => Some("IFMP"),
+            102 => Some("PNNI"),
+            103 => Some("PIM"),
+            104 => Some("ARIS"),
+            105 => Some("SCPS"),
+            106 => Some("QNX"),
+            107 => Some("A/N"),
+            108 => Some("IPComp"),
+            109 => Some("SNP"),
+            110 => Some("Compaq-Peer"),
+            111 => Some("IPX-in-IP"),
+            112 => Some("VRRP"),
+            113 => Some("PGM"),
+            114 => None,
+            115 => Some("L2TP"),
+            116 => Some("DDX"),
+            117 => Some("IATP"),
+            118 => Some("STP"),
+            119 => Some("SRP"),
+            120 => Some("UTI"),
+            121 => Some("SMP"),
+            122 => Some("SM (deprecated)"),
+            123 => Some("PTP"),
+            124 => Some("ISIS over IPv4"),
+            125 => Some("FIRE"),
+            126 => Some("CRTP"),
+            127 => Some("CRUDP"),
+            128 => Some("SSCOPMCE"),
+            129 => Some("IPLT"),
+            130 => Some("SPS"),
+            131 => Some("PIPE"),
+            132 => Some("SCTP"),
+            133 => Some("FC"),
+            134 => Some("RSVP-E2E-IGNORE"),
+            135 => Some("Mobility Header"),
+            136 => Some("UDPLite"),
+            137 => Some("MPLS-in-IP"),
+            138 => Some("manet"),
+            139 => Some("HIP"),
+            140 => Some("Shim6"),
+            141 => Some("WESP"),
+            142 => Some("ROHC"),
+            143 => Some("Ethernet"),
+            144 => Some("AGGFRAG"),
+            145..=252 => None,
+            253 => None,
+            254 => None,
+            255 => Some("Reserved"),
+        }
+    }
+
+    /// Returns the "protocol" string if known. Usually this the non abbreviated name of the protocol.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use etherparse::IpNumber;
+    ///
+    /// assert_eq!(IpNumber::UDP.protocol_str(), Some("User Datagram"));
+    ///
+    /// // Unassigned values return None
+    /// assert_eq!(IpNumber(145).protocol_str(), None);
+    /// ```
+    ///
+    /// # Data Source
+    ///
+    /// The string was copied from <https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml>
+    /// on 2023-04-11.
+    pub fn protocol_str(self) -> Option<&'static str> {
+        // auto generated from CSV
+        // https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
+        // on 2023-04-11.
+        match self.0 {
+            0 => Some("IPv6 Hop-by-Hop Option"),
+            1 => Some("Internet Control Message"),
+            2 => Some("Internet Group Management"),
+            3 => Some("Gateway-to-Gateway"),
+            4 => Some("IPv4 encapsulation"),
+            5 => Some("Stream"),
+            6 => Some("Transmission Control"),
+            7 => Some("CBT"),
+            8 => Some("Exterior Gateway Protocol"),
+            9 => Some("any private interior gateway (used by Cisco for their IGRP)"),
+            10 => Some("BBN RCC Monitoring"),
+            11 => Some("Network Voice Protocol"),
+            12 => Some("PUP"),
+            13 => Some("ARGUS"),
+            14 => Some("EMCON"),
+            15 => Some("Cross Net Debugger"),
+            16 => Some("Chaos"),
+            17 => Some("User Datagram"),
+            18 => Some("Multiplexing"),
+            19 => Some("DCN Measurement Subsystems"),
+            20 => Some("Host Monitoring"),
+            21 => Some("Packet Radio Measurement"),
+            22 => Some("XEROX NS IDP"),
+            23 => Some("Trunk-1"),
+            24 => Some("Trunk-2"),
+            25 => Some("Leaf-1"),
+            26 => Some("Leaf-2"),
+            27 => Some("Reliable Data Protocol"),
+            28 => Some("Internet Reliable Transaction"),
+            29 => Some("ISO Transport Protocol Class 4"),
+            30 => Some("Bulk Data Transfer Protocol"),
+            31 => Some("MFE Network Services Protocol"),
+            32 => Some("MERIT Internodal Protocol"),
+            33 => Some("Datagram Congestion Control Protocol"),
+            34 => Some("Third Party Connect Protocol"),
+            35 => Some("Inter-Domain Policy Routing Protocol"),
+            36 => Some("XTP"),
+            37 => Some("Datagram Delivery Protocol"),
+            38 => Some("IDPR Control Message Transport Proto"),
+            39 => Some("TP++ Transport Protocol"),
+            40 => Some("IL Transport Protocol"),
+            41 => Some("IPv6 encapsulation"),
+            42 => Some("Source Demand Routing Protocol"),
+            43 => Some("Routing Header for IPv6"),
+            44 => Some("Fragment Header for IPv6"),
+            45 => Some("Inter-Domain Routing Protocol"),
+            46 => Some("Reservation Protocol"),
+            47 => Some("Generic Routing Encapsulation"),
+            48 => Some("Dynamic Source Routing Protocol"),
+            49 => Some("BNA"),
+            50 => Some("Encap Security Payload"),
+            51 => Some("Authentication Header"),
+            52 => Some("Integrated Net Layer Security  TUBA"),
+            53 => Some("IP with Encryption"),
+            54 => Some("NBMA Address Resolution Protocol"),
+            55 => Some("IP Mobility"),
+            56 => Some("Transport Layer Security Protocol using Kryptonet key management"),
+            57 => Some("SKIP"),
+            58 => Some("ICMP for IPv6"),
+            59 => Some("No Next Header for IPv6"),
+            60 => Some("Destination Options for IPv6"),
+            61 => Some("any host internal protocol"),
+            62 => Some("CFTP"),
+            63 => Some("any local network"),
+            64 => Some("SATNET and Backroom EXPAK"),
+            65 => Some("Kryptolan"),
+            66 => Some("MIT Remote Virtual Disk Protocol"),
+            67 => Some("Internet Pluribus Packet Core"),
+            68 => Some("any distributed file system"),
+            69 => Some("SATNET Monitoring"),
+            70 => Some("VISA Protocol"),
+            71 => Some("Internet Packet Core Utility"),
+            72 => Some("Computer Protocol Network Executive"),
+            73 => Some("Computer Protocol Heart Beat"),
+            74 => Some("Wang Span Network"),
+            75 => Some("Packet Video Protocol"),
+            76 => Some("Backroom SATNET Monitoring"),
+            77 => Some("SUN ND PROTOCOL-Temporary"),
+            78 => Some("WIDEBAND Monitoring"),
+            79 => Some("WIDEBAND EXPAK"),
+            80 => Some("ISO Internet Protocol"),
+            81 => Some("VMTP"),
+            82 => Some("SECURE-VMTP"),
+            83 => Some("VINES"),
+            84 => Some("Internet Protocol Traffic Manager"),
+            85 => Some("NSFNET-IGP"),
+            86 => Some("Dissimilar Gateway Protocol"),
+            87 => Some("TCF"),
+            88 => Some("EIGRP"),
+            89 => Some("OSPFIGP"),
+            90 => Some("Sprite RPC Protocol"),
+            91 => Some("Locus Address Resolution Protocol"),
+            92 => Some("Multicast Transport Protocol"),
+            93 => Some("AX.25 Frames"),
+            94 => Some("IP-within-IP Encapsulation Protocol"),
+            95 => Some("Mobile Internetworking Control Pro."),
+            96 => Some("Semaphore Communications Sec. Pro."),
+            97 => Some("Ethernet-within-IP Encapsulation"),
+            98 => Some("Encapsulation Header"),
+            99 => Some("any private encryption scheme"),
+            100 => Some("GMTP"),
+            101 => Some("Ipsilon Flow Management Protocol"),
+            102 => Some("PNNI over IP"),
+            103 => Some("Protocol Independent Multicast"),
+            104 => Some("ARIS"),
+            105 => Some("SCPS"),
+            106 => Some("QNX"),
+            107 => Some("Active Networks"),
+            108 => Some("IP Payload Compression Protocol"),
+            109 => Some("Sitara Networks Protocol"),
+            110 => Some("Compaq Peer Protocol"),
+            111 => Some("IPX in IP"),
+            112 => Some("Virtual Router Redundancy Protocol"),
+            113 => Some("PGM Reliable Transport Protocol"),
+            114 => Some("any 0-hop protocol"),
+            115 => Some("Layer Two Tunneling Protocol"),
+            116 => Some("D-II Data Exchange (DDX)"),
+            117 => Some("Interactive Agent Transfer Protocol"),
+            118 => Some("Schedule Transfer Protocol"),
+            119 => Some("SpectraLink Radio Protocol"),
+            120 => Some("UTI"),
+            121 => Some("Simple Message Protocol"),
+            122 => Some("Simple Multicast Protocol"),
+            123 => Some("Performance Transparency Protocol"),
+            124 => None,
+            125 => None,
+            126 => Some("Combat Radio Transport Protocol"),
+            127 => Some("Combat Radio User Datagram"),
+            128 => None,
+            129 => None,
+            130 => Some("Secure Packet Shield"),
+            131 => Some("Private IP Encapsulation within IP"),
+            132 => Some("Stream Control Transmission Protocol"),
+            133 => Some("Fibre Channel"),
+            134 => None,
+            135 => None,
+            136 => None,
+            137 => None,
+            138 => Some("MANET Protocols"),
+            139 => Some("Host Identity Protocol"),
+            140 => Some("Shim6 Protocol"),
+            141 => Some("Wrapped Encapsulating Security Payload"),
+            142 => Some("Robust Header Compression"),
+            143 => Some("Ethernet"),
+            144 => Some("AGGFRAG encapsulation payload for ESP"),
+            145..=252 => None,
+            253 => Some("Use for experimentation and testing"),
+            254 => Some("Use for experimentation and testing"),
+            255 => None,
+        }
+    }
+}
+
+impl Default for IpNumber {
+    #[inline]
+    fn default() -> Self {
+        // 255 chosen as it is not used by any
+        // protocol and is reserved.
+        Self(255)
+    }
+}
+
+impl From<u8> for IpNumber {
+    #[inline]
+    fn from(val: u8) -> Self {
+        Self(val)
+    }
+}
+
+impl From<IpNumber> for u8 {
+    #[inline]
+    fn from(val: IpNumber) -> Self {
+        val.0
+    }
+}
+
+impl core::fmt::Debug for IpNumber {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        if let Some(keyword) = self.keyword_str() {
+            if let Some(protocol) = self.protocol_str() {
+                write!(f, "{} ({} - {})", self.0, keyword, protocol)
+            } else {
+                write!(f, "{} ({})", self.0, keyword)
+            }
+        } else if let Some(protocol) = self.protocol_str() {
+            write!(f, "{} ({})", self.0, protocol)
+        } else {
+            write!(f, "{}", self.0)
+        }
+    }
+}
+
+/// Constants for the ip protocol numbers for easy importing (e.g. `use ip_number::*;`).
+///
+/// The constants only exist for convenience so you can import them
+/// (`use ip_number::*`) without a need to write `IpNumber::` in front
+/// of every constant.
+///
+/// You can access the underlying `u8` value by using `.0` and any `u8`
+/// can be converted to an `IpNumber`:
+///
+/// ```
+/// use etherparse::{ip_number::TCP, IpNumber};
+///
+/// assert_eq!(TCP.0, 6);
+/// assert_eq!(TCP, IpNumber(6));
+/// let num: IpNumber = 6.into();
+/// assert_eq!(TCP, num);
+/// ```
+///
+/// The list original values were copied from
+/// <https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml>
+pub mod ip_number {
+    use crate::IpNumber;
+
+    /// IPv6 Hop-by-Hop Option \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\]
+    pub const IPV6_HOP_BY_HOP: IpNumber = IpNumber::IPV6_HEADER_HOP_BY_HOP; // 0
+    /// IPv6 Hop-by-Hop Option \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\]
+    pub const IPV6_HEADER_HOP_BY_HOP: IpNumber = IpNumber::IPV6_HEADER_HOP_BY_HOP; // 0
+    /// Internet Control Message \[[RFC792](https://datatracker.ietf.org/doc/html/rfc792)\]
+    pub const ICMP: IpNumber = IpNumber::ICMP; // 1
+    /// Internet Group Management \[[RFC1112](https://datatracker.ietf.org/doc/html/rfc1112)\]
+    pub const IGMP: IpNumber = IpNumber::IGMP; // 2
+    /// Gateway-to-Gateway \[[RFC823](https://datatracker.ietf.org/doc/html/rfc823)\]
+    pub const GGP: IpNumber = IpNumber::GGP; // 3
+    /// IPv4 encapsulation \[[RFC2003](https://datatracker.ietf.org/doc/html/rfc2003)\]
+    pub const IPV4: IpNumber = IpNumber::IPV4; // 4
+    /// Stream \[[RFC1190](https://datatracker.ietf.org/doc/html/rfc1190)\] \[[RFC1819](https://datatracker.ietf.org/doc/html/rfc1819)\]
+    pub const STREAM: IpNumber = IpNumber::STREAM; // 5
+    /// Transmission Control \[[RFC793](https://datatracker.ietf.org/doc/html/rfc793)\]
+    pub const TCP: IpNumber = IpNumber::TCP; // 6
+    /// CBT \[Tony_Ballardie\]
+    pub const CBT: IpNumber = IpNumber::CBT; // 7
+    /// Exterior Gateway Protocol \[[RFC888](https://datatracker.ietf.org/doc/html/rfc888)\] \[David_Mills\]
+    pub const EGP: IpNumber = IpNumber::EGP; // 8
+    /// any private interior gateway (used by Cisco for their IGRP) \[Internet_Assigned_Numbers_Authority\]
+    pub const IGP: IpNumber = IpNumber::IGP; // 9
+    /// BBN RCC Monitoring \[Steve_Chipman\]
+    pub const BBN_RCC_MON: IpNumber = IpNumber::BBN_RCC_MON; // 10
+    /// Network Voice Protocol \[[RFC741](https://datatracker.ietf.org/doc/html/rfc741)\]\[Steve_Casner\]
+    pub const NVP_II: IpNumber = IpNumber::NVP_II; // 11
+    /// PUP
+    pub const PUP: IpNumber = IpNumber::PUP; // 12
+    /// ARGUS (deprecated) \[Robert_W_Scheifler\]
+    pub const ARGUS: IpNumber = IpNumber::ARGUS; // 13
+    /// EMCON \[mystery contact\]
+    pub const EMCON: IpNumber = IpNumber::EMCON; // 14
+    /// Cross Net Debugger \[Haverty, J., "XNET Formats for Internet Protocol Version 4", IEN 158, October 1980.\]\[Jack_Haverty\]
+    pub const XNET: IpNumber = IpNumber::XNET; // 15
+    /// Chaos \[J_Noel_Chiappa\]
+    pub const CHAOS: IpNumber = IpNumber::CHAOS; // 16
+    /// User Datagram \[[RFC768](https://datatracker.ietf.org/doc/html/rfc768)\] \[Jon_Postel\]
+    pub const UDP: IpNumber = IpNumber::UDP; // 17
+    /// Multiplexing \[Cohen, D. and J. Postel, "Multiplexing Protocol", IEN 90, USC/Information Sciences Institute, May 1979.\]\[Jon_Postel\]
+    pub const MUX: IpNumber = IpNumber::MUX; // 18
+    /// DCN Measurement Subsystems \[David_Mills\]
+    pub const DCN_MEAS: IpNumber = IpNumber::DCN_MEAS; // 19
+    /// Host Monitoring \[[RFC869](https://datatracker.ietf.org/doc/html/rfc869)\]\[Bob_Hinden\]
+    pub const HMP: IpNumber = IpNumber::HMP; // 20
+    /// Packet Radio Measurement \[Zaw_Sing_Su\]
+    pub const PRM: IpNumber = IpNumber::PRM; // 21
+    /// XEROX NS IDP
+    pub const XNS_IDP: IpNumber = IpNumber::XNS_IDP; // 22
+    /// Trunk-1 \[Barry_Boehm\]
+    pub const TRUNK1: IpNumber = IpNumber::TRUNK1; // 23
+    /// Trunk-2 \[Barry_Boehm\]
+    pub const TRUNK2: IpNumber = IpNumber::TRUNK2; // 24
+    /// Leaf-1 \[Barry_Boehm\]
+    pub const LEAF1: IpNumber = IpNumber::LEAF1; // 25
+    /// Leaf-2 \[Barry_Boehm\]
+    pub const LEAF2: IpNumber = IpNumber::LEAF2; // 26
+    /// Reliable Data Protocol \[[RFC908](https://datatracker.ietf.org/doc/html/rfc908)\] \[Bob_Hinden\]
+    pub const RDP: IpNumber = IpNumber::RDP; // 27
+    /// Internet Reliable Transaction \[[RFC938](https://datatracker.ietf.org/doc/html/rfc938)\] \[Trudy_Miller\]
+    pub const IRTP: IpNumber = IpNumber::IRTP; // 28
+    /// ISO Transport Protocol Class 4 \[[RFC905](https://datatracker.ietf.org/doc/html/rfc905)\] \[mystery contact\]
+    pub const ISO_TP4: IpNumber = IpNumber::ISO_TP4; // 29
+    /// Bulk Data Transfer Protocol \[[RFC969](https://datatracker.ietf.org/doc/html/rfc969)\] \[David_Clark\]
+    pub const NET_BLT: IpNumber = IpNumber::NET_BLT; // 30
+    /// MFE Network Services Protocol \[Shuttleworth, B., "A Documentary of MFENet, a National Computer Network", UCRL-52317, Lawrence Livermore Labs, Livermore, California, June 1977.\] \[Barry_Howard\]
+    pub const MFE_NSP: IpNumber = IpNumber::MFE_NSP; // 31
+    /// MERIT Internodal Protocol \[Hans_Werner_Braun\]
+    pub const MERIT_INP: IpNumber = IpNumber::MERIT_INP; // 32
+    /// Datagram Congestion Control Protocol \[[RFC4340](https://datatracker.ietf.org/doc/html/rfc4340)\]
+    pub const DCCP: IpNumber = IpNumber::DCCP; // 33
+    /// Third Party Connect Protocol \[Stuart_A_Friedberg\]
+    pub const THIRD_PARTY_CONNECT_PROTOCOL: IpNumber = IpNumber::THIRD_PARTY_CONNECT_PROTOCOL; // 34
+    /// Inter-Domain Policy Routing Protocol \[Martha_Steenstrup\]
+    pub const IDPR: IpNumber = IpNumber::IDPR; // 35
+    /// XTP \[Greg_Chesson\]
+    pub const XTP: IpNumber = IpNumber::XTP; // 36
+    /// Datagram Delivery Protocol \[Wesley_Craig\]
+    pub const DDP: IpNumber = IpNumber::DDP; // 37
+    /// IDPR Control Message Transport Proto \[Martha_Steenstrup\]
+    pub const IDPR_CMTP: IpNumber = IpNumber::IDPR_CMTP; // 38
+    /// TP++ Transport Protocol \[Dirk_Fromhein\]
+    pub const TP_PLUS_PLUS: IpNumber = IpNumber::TP_PLUS_PLUS; // 39
+    /// IL Transport Protocol \[Dave_Presotto\]
+    pub const IL: IpNumber = IpNumber::IL; // 40
+    /// IPv6 encapsulation \[[RFC2473](https://datatracker.ietf.org/doc/html/rfc2473)\]
+    pub const IPV6: IpNumber = IpNumber::IPV6; // 41
+    /// Source Demand Routing Protocol \[Deborah_Estrin\]
+    pub const SDRP: IpNumber = IpNumber::SDRP; // 42
+    /// Routing Header for IPv6 \[Steve_Deering\]
+    pub const IPV6_ROUTE_HEADER: IpNumber = IpNumber::IPV6_ROUTE_HEADER; // 43
+    /// Routing Header for IPv6 \[Steve_Deering\]
+    pub const IPV6_ROUTE: IpNumber = IpNumber::IPV6_ROUTE_HEADER; // 43
+    /// Fragment Header for IPv6 \[Steve_Deering\]
+    pub const IPV6_FRAGMENTATION_HEADER: IpNumber = IpNumber::IPV6_FRAGMENTATION_HEADER; // 44
+    /// Fragment Header for IPv6 \[Steve_Deering\]
+    pub const IPV6_FRAG: IpNumber = IpNumber::IPV6_FRAGMENTATION_HEADER; // 44
+    /// Inter-Domain Routing Protocol \[Sue_Hares\]
+    pub const IDRP: IpNumber = IpNumber::IDRP; // 45
+    /// Reservation Protocol \[[RFC2205](https://datatracker.ietf.org/doc/html/rfc2205)\]\[[RFC3209](https://datatracker.ietf.org/doc/html/rfc3209)\]\[Bob_Braden\]
+    pub const RSVP: IpNumber = IpNumber::RSVP; // 46
+    /// Generic Routing Encapsulation \[[RFC2784](https://datatracker.ietf.org/doc/html/rfc2784)\]\[Tony_Li\]
+    pub const GRE: IpNumber = IpNumber::GRE; // 47
+    /// Dynamic Source Routing Protocol \[[RFC4728](https://datatracker.ietf.org/doc/html/rfc4728)\]
+    pub const DSR: IpNumber = IpNumber::DSR; // 48
+    /// BNA \[Gary Salamon\]
+    pub const BNA: IpNumber = IpNumber::BNA; // 49
+    /// Encapsulating Security Payload \[[RFC4303](https://datatracker.ietf.org/doc/html/rfc4303)\]
+    pub const ENCAP_SEC: IpNumber = IpNumber::ENCAPSULATING_SECURITY_PAYLOAD; // 50
+    /// Encapsulating Security Payload \[[RFC4303](https://datatracker.ietf.org/doc/html/rfc4303)\]
+    pub const ENCAPSULATING_SECURITY_PAYLOAD: IpNumber = IpNumber::ENCAPSULATING_SECURITY_PAYLOAD; // 50
+    /// Authentication Header \[[RFC4302](https://datatracker.ietf.org/doc/html/rfc4302)\]
+    pub const AUTH: IpNumber = IpNumber::AUTHENTICATION_HEADER; // 51
+    /// Authentication Header \[[RFC4302](https://datatracker.ietf.org/doc/html/rfc4302)\]
+    pub const AUTHENTICATION_HEADER: IpNumber = IpNumber::AUTHENTICATION_HEADER; // 51
+    /// Integrated Net Layer Security  TUBA \[K_Robert_Glenn\]
+    pub const INLSP: IpNumber = IpNumber::INLSP; // 52
+    /// IP with Encryption (deprecated) \[John_Ioannidis\]
+    pub const SWIPE: IpNumber = IpNumber::SWIPE; // 53
+    /// NBMA Address Resolution Protocol \[[RFC1735](https://datatracker.ietf.org/doc/html/rfc1735)\]
+    pub const NARP: IpNumber = IpNumber::NARP; // 54
+    /// IP Mobility \[Charlie_Perkins\]
+    pub const MOBILE: IpNumber = IpNumber::MOBILE; // 55
+    /// Transport Layer Security Protocol using Kryptonet key management \[Christer_Oberg\]
+    pub const TLSP: IpNumber = IpNumber::TLSP; // 56
+    /// SKIP \[Tom_Markson\]
+    pub const SKIP: IpNumber = IpNumber::SKIP; // 57
+    /// IPv6 ICMP next-header type \[[RFC4443](https://datatracker.ietf.org/doc/html/rfc4443)\]
+    pub const IPV6_ICMP: IpNumber = IpNumber::IPV6_ICMP; // 58
+    /// No Next Header for IPv6 \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\]
+    pub const IPV6_NO_NEXT_HEADER: IpNumber = IpNumber::IPV6_NO_NEXT_HEADER; // 59
+    /// Destination Options for IPv6 \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\]
+    pub const IPV6_DEST_OPTIONS: IpNumber = IpNumber::IPV6_DESTINATION_OPTIONS; // 60
+    /// Destination Options for IPv6 \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\]
+    pub const IPV6_DESTINATION_OPTIONS: IpNumber = IpNumber::IPV6_DESTINATION_OPTIONS; // 60
+    /// any host internal protocol \[Internet_Assigned_Numbers_Authority\]
+    pub const ANY_HOST_INTERNAL_PROTOCOL: IpNumber = IpNumber::ANY_HOST_INTERNAL_PROTOCOL; // 61
+    /// CFTP \[Forsdick, H., "CFTP", Network Message, Bolt Beranek and Newman, January 1982.\]\[Harry_Forsdick\]
+    pub const CFTP: IpNumber = IpNumber::CFTP; // 62
+    /// any local network \[Internet_Assigned_Numbers_Authority\]
+    pub const ANY_LOCAL_NETWORK: IpNumber = IpNumber::ANY_LOCAL_NETWORK; // 63
+    /// SATNET and Backroom EXPAK \[Steven_Blumenthal\]
+    pub const SAT_EXPAK: IpNumber = IpNumber::SAT_EXPAK; // 64
+    /// Kryptolan \[Paul Liu\]
+    pub const KRYTOLAN: IpNumber = IpNumber::KRYTOLAN; // 65
+    /// MIT Remote Virtual Disk Protocol \[Michael_Greenwald\]
+    pub const RVD: IpNumber = IpNumber::RVD; // 66
+    /// Internet Pluribus Packet Core \[Steven_Blumenthal\]
+    pub const IPPC: IpNumber = IpNumber::IPPC; // 67
+    /// any distributed file system \[Internet_Assigned_Numbers_Authority\]
+    pub const ANY_DISTRIBUTED_FILE_SYSTEM: IpNumber = IpNumber::ANY_DISTRIBUTED_FILE_SYSTEM; // 68
+    /// SATNET Monitoring \[Steven_Blumenthal\]
+    pub const SAT_MON: IpNumber = IpNumber::SAT_MON; // 69
+    /// VISA Protocol \[Gene_Tsudik\]
+    pub const VISA: IpNumber = IpNumber::VISA; // 70
+    /// Internet Packet Core Utility \[Steven_Blumenthal\]
+    pub const IPCV: IpNumber = IpNumber::IPCV; // 71
+    /// Computer Protocol Network Executive \[David Mittnacht\]
+    pub const CPNX: IpNumber = IpNumber::CPNX; // 72
+    /// Computer Protocol Heart Beat \[David Mittnacht\]
+    pub const CPHB: IpNumber = IpNumber::CPHB; // 73
+    /// Wang Span Network \[Victor Dafoulas\]
+    pub const WSN: IpNumber = IpNumber::WSN; // 74
+    /// Packet Video Protocol \[Steve_Casner\]
+    pub const PVP: IpNumber = IpNumber::PVP; // 75
+    /// Backroom SATNET Monitoring \[Steven_Blumenthal\]
+    pub const BR_SAT_MON: IpNumber = IpNumber::BR_SAT_MON; // 76
+    /// SUN ND PROTOCOL-Temporary \[William_Melohn\]
+    pub const SUN_ND: IpNumber = IpNumber::SUN_ND; // 77
+    /// WIDEBAND Monitoring \[Steven_Blumenthal\]
+    pub const WB_MON: IpNumber = IpNumber::WB_MON; // 78
+    /// WIDEBAND EXPAK \[Steven_Blumenthal\]
+    pub const WB_EXPAK: IpNumber = IpNumber::WB_EXPAK; // 79
+    /// ISO Internet Protocol \[Marshall_T_Rose\]
+    pub const ISO_IP: IpNumber = IpNumber::ISO_IP; // 80
+    /// VMTP \[Dave_Cheriton\]
+    pub const VMTP: IpNumber = IpNumber::VMTP; // 81
+    /// SECURE-VMTP \[Dave_Cheriton\]
+    pub const SECURE_VMTP: IpNumber = IpNumber::SECURE_VMTP; // 82
+    /// VINES \[Brian Horn\]
+    pub const VINES: IpNumber = IpNumber::VINES; // 83
+    /// Transaction Transport Protocol or Internet Protocol Traffic Manager \[Jim_Stevens\]
+    pub const TTP_OR_IPTM: IpNumber = IpNumber::TTP_OR_IPTM; // 84
+    /// NSFNET-IGP \[Hans_Werner_Braun\]
+    pub const NSFNET_IGP: IpNumber = IpNumber::NSFNET_IGP; // 85
+    /// Dissimilar Gateway Protocol \[M/A-COM Government Systems, "Dissimilar Gateway Protocol Specification, Draft Version", Contract no. CS901145, November 16, 1987.\]\[Mike_Little\]
+    pub const DGP: IpNumber = IpNumber::DGP; // 86
+    /// TCF \[Guillermo_A_Loyola\]
+    pub const TCF: IpNumber = IpNumber::TCF; // 87
+    /// EIGRP \[[RFC7868](https://datatracker.ietf.org/doc/html/rfc7868)\]
+    pub const EIGRP: IpNumber = IpNumber::EIGRP; // 88
+    /// OSPFIGP \[[RFC1583](https://datatracker.ietf.org/doc/html/rfc1583)\]\[[RFC2328](https://datatracker.ietf.org/doc/html/rfc2328)\]\[[RFC5340](https://datatracker.ietf.org/doc/html/rfc5340)\]\[John_Moy\]
+    pub const OSPFIGP: IpNumber = IpNumber::OSPFIGP; // 89
+    /// Sprite RPC Protocol \[Welch, B., "The Sprite Remote Procedure Call System", Technical Report, UCB/Computer Science Dept., 86/302, University of California at Berkeley, June 1986.\]\[Bruce Willins\]
+    pub const SPRITE_RPC: IpNumber = IpNumber::SPRITE_RPC; // 90
+    /// Locus Address Resolution Protocol \[Brian Horn\]
+    pub const LARP: IpNumber = IpNumber::LARP; // 91
+    /// Multicast Transport Protocol \[Susie_Armstrong\]
+    pub const MTP: IpNumber = IpNumber::MTP; // 92
+    /// AX.25 Frames \[Brian_Kantor\]
+    pub const AX25: IpNumber = IpNumber::AX25; // 93
+    /// IP-within-IP Encapsulation Protocol \[John_Ioannidis\]
+    pub const IPIP: IpNumber = IpNumber::IPIP; // 94
+    /// Mobile Internetworking Control Pro. (deprecated) \[John_Ioannidis\]
+    pub const MICP: IpNumber = IpNumber::MICP; // 95
+    /// Semaphore Communications Sec. Pro. \[Howard_Hart\]
+    pub const SCC_SP: IpNumber = IpNumber::SCC_SP; // 96
+    /// Ethernet-within-IP Encapsulation \[[RFC3378](https://datatracker.ietf.org/doc/html/rfc3378)\]
+    pub const ETHER_IP: IpNumber = IpNumber::ETHER_IP; // 97
+    /// Encapsulation Header \[[RFC1241](https://datatracker.ietf.org/doc/html/rfc1241)\]\[Robert_Woodburn\]
+    pub const ENCAP: IpNumber = IpNumber::ENCAP; // 98
+    /// GMTP \[\[RXB5\]\]
+    pub const GMTP: IpNumber = IpNumber::GMTP; // 100
+    /// Ipsilon Flow Management Protocol \[Bob_Hinden\]\[November 1995, 1997.\]
+    pub const IFMP: IpNumber = IpNumber::IFMP; // 101
+    /// PNNI over IP \[Ross_Callon\]
+    pub const PNNI: IpNumber = IpNumber::PNNI; // 102
+    /// Protocol Independent Multicast \[[RFC7761](https://datatracker.ietf.org/doc/html/rfc7761)\]\[Dino_Farinacci\]
+    pub const PIM: IpNumber = IpNumber::PIM; // 103
+    /// ARIS \[Nancy_Feldman\]
+    pub const ARIS: IpNumber = IpNumber::ARIS; // 104
+    /// SCPS \[Robert_Durst\]
+    pub const SCPS: IpNumber = IpNumber::SCPS; // 105
+    /// QNX \[Michael_Hunter\]
+    pub const QNX: IpNumber = IpNumber::QNX; // 106
+    /// Active Networks \[Bob_Braden\]
+    pub const ACTIVE_NETWORKS: IpNumber = IpNumber::ACTIVE_NETWORKS; // 107
+    /// IP Payload Compression Protocol \[[RFC2393](https://datatracker.ietf.org/doc/html/rfc2393)\]
+    pub const IP_COMP: IpNumber = IpNumber::IP_COMP; // 108
+    /// Sitara Networks Protocol \[Manickam_R_Sridhar\]
+    pub const SITRA_NETWORKS_PROTOCOL: IpNumber = IpNumber::SITRA_NETWORKS_PROTOCOL; // 109
+    /// Compaq Peer Protocol \[Victor_Volpe\]
+    pub const COMPAQ_PEER: IpNumber = IpNumber::COMPAQ_PEER; // 110
+    /// IPX in IP \[CJ_Lee\]
+    pub const IPX_IN_IP: IpNumber = IpNumber::IPX_IN_IP; // 111
+    /// Virtual Router Redundancy Protocol \[[RFC5798](https://datatracker.ietf.org/doc/html/rfc5798)\]
+    pub const VRRP: IpNumber = IpNumber::VRRP; // 112
+    /// PGM Reliable Transport Protocol \[Tony_Speakman\]
+    pub const PGM: IpNumber = IpNumber::PGM; // 113
+    /// any 0-hop protocol \[Internet_Assigned_Numbers_Authority\]
+    pub const ANY_ZERO_HOP_PROTOCOL: IpNumber = IpNumber::ANY_ZERO_HOP_PROTOCOL; // 114
+    /// Layer Two Tunneling Protocol \[[RFC3931](https://datatracker.ietf.org/doc/html/rfc3931)\]\[Bernard_Aboba\]
+    pub const LAYER2_TUNNELING_PROTOCOL: IpNumber = IpNumber::LAYER2_TUNNELING_PROTOCOL; // 115
+    /// D-II Data Exchange (DDX) \[John_Worley\]
+    pub const DDX: IpNumber = IpNumber::DDX; // 116
+    /// Interactive Agent Transfer Protocol \[John_Murphy\]
+    pub const IATP: IpNumber = IpNumber::IATP; // 117
+    /// Schedule Transfer Protocol \[Jean_Michel_Pittet\]
+    pub const STP: IpNumber = IpNumber::STP; // 118
+    /// SpectraLink Radio Protocol \[Mark_Hamilton\]
+    pub const SRP: IpNumber = IpNumber::SRP; // 119
+    /// UTI \[Peter_Lothberg\]
+    pub const UTI: IpNumber = IpNumber::UTI; // 120
+    /// Simple Message Protocol \[Leif_Ekblad\]
+    pub const SIMPLE_MESSAGE_PROTOCOL: IpNumber = IpNumber::SIMPLE_MESSAGE_PROTOCOL; // 121
+    /// Simple Multicast Protocol (deprecated) \[Jon_Crowcroft\]\[draft-perlman-simple-multicast\]
+    pub const SM: IpNumber = IpNumber::SM; // 122
+    /// Performance Transparency Protocol \[Michael_Welzl\]
+    pub const PTP: IpNumber = IpNumber::PTP; // 123
+    /// ISIS over IPv4 \[Tony_Przygienda\]
+    pub const ISIS_OVER_IPV4: IpNumber = IpNumber::ISIS_OVER_IPV4; // 124
+    /// FIRE \[Criag_Partridge\]
+    pub const FIRE: IpNumber = IpNumber::FIRE; // 125
+    /// Combat Radio Transport Protocol \[Robert_Sautter\]
+    pub const CRTP: IpNumber = IpNumber::CRTP; // 126
+    /// Combat Radio User Datagram \[Robert_Sautter\]
+    pub const CRUDP: IpNumber = IpNumber::CRUDP; // 127
+    /// SSCOPMCE \[Kurt_Waber\]
+    pub const SSCOPMCE: IpNumber = IpNumber::SSCOPMCE; // 128
+    /// IPLT \[\[Hollbach\]\]
+    pub const IPLT: IpNumber = IpNumber::IPLT; // 129
+    /// Secure Packet Shield \[Bill_McIntosh\]
+    pub const SPS: IpNumber = IpNumber::SPS; // 130
+    /// Private IP Encapsulation within IP \[Bernhard_Petri\]
+    pub const PIPE: IpNumber = IpNumber::PIPE; // 131
+    /// Stream Control Transmission Protocol \[Randall_R_Stewart\]
+    pub const SCTP: IpNumber = IpNumber::SCTP; // 132
+    /// Fibre Channel \[Murali_Rajagopal\]\[[RFC6172](https://datatracker.ietf.org/doc/html/rfc6172)\]
+    pub const FC: IpNumber = IpNumber::FC; // 133
+    /// RSVP-E2E-IGNORE \[[RFC3175](https://datatracker.ietf.org/doc/html/rfc3175)\]
+    pub const RSVP_E2E_IGNORE: IpNumber = IpNumber::RSVP_E2E_IGNORE; // 134
+    /// MobilityHeader \[[RFC6275](https://datatracker.ietf.org/doc/html/rfc6275)\]
+    pub const MOBILITY: IpNumber = IpNumber::MOBILITY_HEADER; // 135
+    /// MobilityHeader \[[RFC6275](https://datatracker.ietf.org/doc/html/rfc6275)\]
+    pub const MOBILITY_HEADER: IpNumber = IpNumber::MOBILITY_HEADER; // 135
+    /// UDPLite \[[RFC3828](https://datatracker.ietf.org/doc/html/rfc3828)\]
+    pub const UDP_LITE: IpNumber = IpNumber::UDP_LITE; // 136
+    /// \[[RFC4023](https://datatracker.ietf.org/doc/html/rfc4023)\]
+    pub const MPLS_IN_IP: IpNumber = IpNumber::MPLS_IN_IP; // 137
+    /// MANET Protocols \[[RFC5498](https://datatracker.ietf.org/doc/html/rfc5498)\]
+    pub const MANET: IpNumber = IpNumber::MANET; // 138
+    /// Host Identity Protocol \[[RFC7401](https://datatracker.ietf.org/doc/html/rfc7401)\]
+    pub const HIP: IpNumber = IpNumber::HIP; // 139
+    /// Shim6 Protocol \[[RFC5533](https://datatracker.ietf.org/doc/html/rfc5533)\]
+    pub const SHIM6: IpNumber = IpNumber::SHIM6; // 140
+    /// Wrapped Encapsulating Security Payload \[[RFC5840](https://datatracker.ietf.org/doc/html/rfc5840)\]
+    pub const WESP: IpNumber = IpNumber::WESP; // 141
+    /// Robust Header Compression \[[RFC5858](https://datatracker.ietf.org/doc/html/rfc5858)\]
+    pub const ROHC: IpNumber = IpNumber::ROHC; // 142
+    /// Use for experimentation and testing
+    pub const EXP0: IpNumber = IpNumber::EXPERIMENTAL_AND_TESTING_0; // 253
+    /// Use for experimentation and testing
+    pub const EXPERIMENTAL_AND_TESTING_0: IpNumber = IpNumber::EXPERIMENTAL_AND_TESTING_0; // 253
+    /// Use for experimentation and testing
+    pub const EXP1: IpNumber = IpNumber::EXPERIMENTAL_AND_TESTING_1; // 254
+    /// Use for experimentation and testing
+    pub const EXPERIMENTAL_AND_TESTING_1: IpNumber = IpNumber::EXPERIMENTAL_AND_TESTING_1;
+    // 254
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use core::{
+        cmp::Ordering,
+        hash::{Hash, Hasher},
+    };
+    use proptest::prelude::*;
+    use std::{collections::hash_map::DefaultHasher, format};
+
+    #[test]
+    fn is_ipv6_ext_header_value() {
+        use crate::ip_number::*;
+        use crate::IpNumber;
+        let ext_ids = [
+            IPV6_HOP_BY_HOP,
+            IPV6_ROUTE,
+            IPV6_FRAG,
+            ENCAP_SEC,
+            AUTH,
+            IPV6_DEST_OPTIONS,
+            MOBILITY,
+            HIP,
+            SHIM6,
+            EXP0,
+            EXP1,
+        ];
+
+        for i in 0..std::u8::MAX {
+            assert_eq!(
+                ext_ids.contains(&IpNumber(i)),
+                IpNumber(i).is_ipv6_ext_header_value()
+            );
+        }
+    }
+
+    #[test]
+    fn keyword_str() {
+        // auto generated from CSV
+        // https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
+        // on 2023-04-11.
+        assert_eq!(IpNumber(0).keyword_str(), Some("HOPOPT"));
+        assert_eq!(IpNumber(1).keyword_str(), Some("ICMP"));
+        assert_eq!(IpNumber(2).keyword_str(), Some("IGMP"));
+        assert_eq!(IpNumber(3).keyword_str(), Some("GGP"));
+        assert_eq!(IpNumber(4).keyword_str(), Some("IPv4"));
+        assert_eq!(IpNumber(5).keyword_str(), Some("ST"));
+        assert_eq!(IpNumber(6).keyword_str(), Some("TCP"));
+        assert_eq!(IpNumber(7).keyword_str(), Some("CBT"));
+        assert_eq!(IpNumber(8).keyword_str(), Some("EGP"));
+        assert_eq!(IpNumber(9).keyword_str(), Some("IGP"));
+        assert_eq!(IpNumber(10).keyword_str(), Some("BBN-RCC-MON"));
+        assert_eq!(IpNumber(11).keyword_str(), Some("NVP-II"));
+        assert_eq!(IpNumber(12).keyword_str(), Some("PUP"));
+        assert_eq!(IpNumber(13).keyword_str(), Some("ARGUS (deprecated)"));
+        assert_eq!(IpNumber(14).keyword_str(), Some("EMCON"));
+        assert_eq!(IpNumber(15).keyword_str(), Some("XNET"));
+        assert_eq!(IpNumber(16).keyword_str(), Some("CHAOS"));
+        assert_eq!(IpNumber(17).keyword_str(), Some("UDP"));
+        assert_eq!(IpNumber(18).keyword_str(), Some("MUX"));
+        assert_eq!(IpNumber(19).keyword_str(), Some("DCN-MEAS"));
+        assert_eq!(IpNumber(20).keyword_str(), Some("HMP"));
+        assert_eq!(IpNumber(21).keyword_str(), Some("PRM"));
+        assert_eq!(IpNumber(22).keyword_str(), Some("XNS-IDP"));
+        assert_eq!(IpNumber(23).keyword_str(), Some("TRUNK-1"));
+        assert_eq!(IpNumber(24).keyword_str(), Some("TRUNK-2"));
+        assert_eq!(IpNumber(25).keyword_str(), Some("LEAF-1"));
+        assert_eq!(IpNumber(26).keyword_str(), Some("LEAF-2"));
+        assert_eq!(IpNumber(27).keyword_str(), Some("RDP"));
+        assert_eq!(IpNumber(28).keyword_str(), Some("IRTP"));
+        assert_eq!(IpNumber(29).keyword_str(), Some("ISO-TP4"));
+        assert_eq!(IpNumber(30).keyword_str(), Some("NETBLT"));
+        assert_eq!(IpNumber(31).keyword_str(), Some("MFE-NSP"));
+        assert_eq!(IpNumber(32).keyword_str(), Some("MERIT-INP"));
+        assert_eq!(IpNumber(33).keyword_str(), Some("DCCP"));
+        assert_eq!(IpNumber(34).keyword_str(), Some("3PC"));
+        assert_eq!(IpNumber(35).keyword_str(), Some("IDPR"));
+        assert_eq!(IpNumber(36).keyword_str(), Some("XTP"));
+        assert_eq!(IpNumber(37).keyword_str(), Some("DDP"));
+        assert_eq!(IpNumber(38).keyword_str(), Some("IDPR-CMTP"));
+        assert_eq!(IpNumber(39).keyword_str(), Some("TP++"));
+        assert_eq!(IpNumber(40).keyword_str(), Some("IL"));
+        assert_eq!(IpNumber(41).keyword_str(), Some("IPv6"));
+        assert_eq!(IpNumber(42).keyword_str(), Some("SDRP"));
+        assert_eq!(IpNumber(43).keyword_str(), Some("IPv6-Route"));
+        assert_eq!(IpNumber(44).keyword_str(), Some("IPv6-Frag"));
+        assert_eq!(IpNumber(45).keyword_str(), Some("IDRP"));
+        assert_eq!(IpNumber(46).keyword_str(), Some("RSVP"));
+        assert_eq!(IpNumber(47).keyword_str(), Some("GRE"));
+        assert_eq!(IpNumber(48).keyword_str(), Some("DSR"));
+        assert_eq!(IpNumber(49).keyword_str(), Some("BNA"));
+        assert_eq!(IpNumber(50).keyword_str(), Some("ESP"));
+        assert_eq!(IpNumber(51).keyword_str(), Some("AH"));
+        assert_eq!(IpNumber(52).keyword_str(), Some("I-NLSP"));
+        assert_eq!(IpNumber(53).keyword_str(), Some("SWIPE (deprecated)"));
+        assert_eq!(IpNumber(54).keyword_str(), Some("NARP"));
+        assert_eq!(IpNumber(55).keyword_str(), Some("MOBILE"));
+        assert_eq!(IpNumber(56).keyword_str(), Some("TLSP"));
+        assert_eq!(IpNumber(57).keyword_str(), Some("SKIP"));
+        assert_eq!(IpNumber(58).keyword_str(), Some("IPv6-ICMP"));
+        assert_eq!(IpNumber(59).keyword_str(), Some("IPv6-NoNxt"));
+        assert_eq!(IpNumber(60).keyword_str(), Some("IPv6-Opts"));
+        assert_eq!(IpNumber(61).keyword_str(), None);
+        assert_eq!(IpNumber(62).keyword_str(), Some("CFTP"));
+        assert_eq!(IpNumber(63).keyword_str(), None);
+        assert_eq!(IpNumber(64).keyword_str(), Some("SAT-EXPAK"));
+        assert_eq!(IpNumber(65).keyword_str(), Some("KRYPTOLAN"));
+        assert_eq!(IpNumber(66).keyword_str(), Some("RVD"));
+        assert_eq!(IpNumber(67).keyword_str(), Some("IPPC"));
+        assert_eq!(IpNumber(68).keyword_str(), None);
+        assert_eq!(IpNumber(69).keyword_str(), Some("SAT-MON"));
+        assert_eq!(IpNumber(70).keyword_str(), Some("VISA"));
+        assert_eq!(IpNumber(71).keyword_str(), Some("IPCV"));
+        assert_eq!(IpNumber(72).keyword_str(), Some("CPNX"));
+        assert_eq!(IpNumber(73).keyword_str(), Some("CPHB"));
+        assert_eq!(IpNumber(74).keyword_str(), Some("WSN"));
+        assert_eq!(IpNumber(75).keyword_str(), Some("PVP"));
+        assert_eq!(IpNumber(76).keyword_str(), Some("BR-SAT-MON"));
+        assert_eq!(IpNumber(77).keyword_str(), Some("SUN-ND"));
+        assert_eq!(IpNumber(78).keyword_str(), Some("WB-MON"));
+        assert_eq!(IpNumber(79).keyword_str(), Some("WB-EXPAK"));
+        assert_eq!(IpNumber(80).keyword_str(), Some("ISO-IP"));
+        assert_eq!(IpNumber(81).keyword_str(), Some("VMTP"));
+        assert_eq!(IpNumber(82).keyword_str(), Some("SECURE-VMTP"));
+        assert_eq!(IpNumber(83).keyword_str(), Some("VINES"));
+        assert_eq!(IpNumber(84).keyword_str(), Some("IPTM"));
+        assert_eq!(IpNumber(85).keyword_str(), Some("NSFNET-IGP"));
+        assert_eq!(IpNumber(86).keyword_str(), Some("DGP"));
+        assert_eq!(IpNumber(87).keyword_str(), Some("TCF"));
+        assert_eq!(IpNumber(88).keyword_str(), Some("EIGRP"));
+        assert_eq!(IpNumber(89).keyword_str(), Some("OSPFIGP"));
+        assert_eq!(IpNumber(90).keyword_str(), Some("Sprite-RPC"));
+        assert_eq!(IpNumber(91).keyword_str(), Some("LARP"));
+        assert_eq!(IpNumber(92).keyword_str(), Some("MTP"));
+        assert_eq!(IpNumber(93).keyword_str(), Some("AX.25"));
+        assert_eq!(IpNumber(94).keyword_str(), Some("IPIP"));
+        assert_eq!(IpNumber(95).keyword_str(), Some("MICP (deprecated)"));
+        assert_eq!(IpNumber(96).keyword_str(), Some("SCC-SP"));
+        assert_eq!(IpNumber(97).keyword_str(), Some("ETHERIP"));
+        assert_eq!(IpNumber(98).keyword_str(), Some("ENCAP"));
+        assert_eq!(IpNumber(99).keyword_str(), None);
+        assert_eq!(IpNumber(100).keyword_str(), Some("GMTP"));
+        assert_eq!(IpNumber(101).keyword_str(), Some("IFMP"));
+        assert_eq!(IpNumber(102).keyword_str(), Some("PNNI"));
+        assert_eq!(IpNumber(103).keyword_str(), Some("PIM"));
+        assert_eq!(IpNumber(104).keyword_str(), Some("ARIS"));
+        assert_eq!(IpNumber(105).keyword_str(), Some("SCPS"));
+        assert_eq!(IpNumber(106).keyword_str(), Some("QNX"));
+        assert_eq!(IpNumber(107).keyword_str(), Some("A/N"));
+        assert_eq!(IpNumber(108).keyword_str(), Some("IPComp"));
+        assert_eq!(IpNumber(109).keyword_str(), Some("SNP"));
+        assert_eq!(IpNumber(110).keyword_str(), Some("Compaq-Peer"));
+        assert_eq!(IpNumber(111).keyword_str(), Some("IPX-in-IP"));
+        assert_eq!(IpNumber(112).keyword_str(), Some("VRRP"));
+        assert_eq!(IpNumber(113).keyword_str(), Some("PGM"));
+        assert_eq!(IpNumber(114).keyword_str(), None);
+        assert_eq!(IpNumber(115).keyword_str(), Some("L2TP"));
+        assert_eq!(IpNumber(116).keyword_str(), Some("DDX"));
+        assert_eq!(IpNumber(117).keyword_str(), Some("IATP"));
+        assert_eq!(IpNumber(118).keyword_str(), Some("STP"));
+        assert_eq!(IpNumber(119).keyword_str(), Some("SRP"));
+        assert_eq!(IpNumber(120).keyword_str(), Some("UTI"));
+        assert_eq!(IpNumber(121).keyword_str(), Some("SMP"));
+        assert_eq!(IpNumber(122).keyword_str(), Some("SM (deprecated)"));
+        assert_eq!(IpNumber(123).keyword_str(), Some("PTP"));
+        assert_eq!(IpNumber(124).keyword_str(), Some("ISIS over IPv4"));
+        assert_eq!(IpNumber(125).keyword_str(), Some("FIRE"));
+        assert_eq!(IpNumber(126).keyword_str(), Some("CRTP"));
+        assert_eq!(IpNumber(127).keyword_str(), Some("CRUDP"));
+        assert_eq!(IpNumber(128).keyword_str(), Some("SSCOPMCE"));
+        assert_eq!(IpNumber(129).keyword_str(), Some("IPLT"));
+        assert_eq!(IpNumber(130).keyword_str(), Some("SPS"));
+        assert_eq!(IpNumber(131).keyword_str(), Some("PIPE"));
+        assert_eq!(IpNumber(132).keyword_str(), Some("SCTP"));
+        assert_eq!(IpNumber(133).keyword_str(), Some("FC"));
+        assert_eq!(IpNumber(134).keyword_str(), Some("RSVP-E2E-IGNORE"));
+        assert_eq!(IpNumber(135).keyword_str(), Some("Mobility Header"));
+        assert_eq!(IpNumber(136).keyword_str(), Some("UDPLite"));
+        assert_eq!(IpNumber(137).keyword_str(), Some("MPLS-in-IP"));
+        assert_eq!(IpNumber(138).keyword_str(), Some("manet"));
+        assert_eq!(IpNumber(139).keyword_str(), Some("HIP"));
+        assert_eq!(IpNumber(140).keyword_str(), Some("Shim6"));
+        assert_eq!(IpNumber(141).keyword_str(), Some("WESP"));
+        assert_eq!(IpNumber(142).keyword_str(), Some("ROHC"));
+        assert_eq!(IpNumber(143).keyword_str(), Some("Ethernet"));
+        assert_eq!(IpNumber(144).keyword_str(), Some("AGGFRAG"));
+        for i in 145u8..=252 {
+            assert_eq!(IpNumber(i).keyword_str(), None);
+        }
+        assert_eq!(IpNumber(253).keyword_str(), None);
+        assert_eq!(IpNumber(254).keyword_str(), None);
+        assert_eq!(IpNumber(255).keyword_str(), Some("Reserved"));
+    }
+
+    #[test]
+    fn protocol_str() {
+        // auto generated from CSV
+        // https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
+        // on 2023-04-11.
+        assert_eq!(IpNumber(0).protocol_str(), Some("IPv6 Hop-by-Hop Option"));
+        assert_eq!(IpNumber(1).protocol_str(), Some("Internet Control Message"));
+        assert_eq!(
+            IpNumber(2).protocol_str(),
+            Some("Internet Group Management")
+        );
+        assert_eq!(IpNumber(3).protocol_str(), Some("Gateway-to-Gateway"));
+        assert_eq!(IpNumber(4).protocol_str(), Some("IPv4 encapsulation"));
+        assert_eq!(IpNumber(5).protocol_str(), Some("Stream"));
+        assert_eq!(IpNumber(6).protocol_str(), Some("Transmission Control"));
+        assert_eq!(IpNumber(7).protocol_str(), Some("CBT"));
+        assert_eq!(
+            IpNumber(8).protocol_str(),
+            Some("Exterior Gateway Protocol")
+        );
+        assert_eq!(
+            IpNumber(9).protocol_str(),
+            Some("any private interior gateway (used by Cisco for their IGRP)")
+        );
+        assert_eq!(IpNumber(10).protocol_str(), Some("BBN RCC Monitoring"));
+        assert_eq!(IpNumber(11).protocol_str(), Some("Network Voice Protocol"));
+        assert_eq!(IpNumber(12).protocol_str(), Some("PUP"));
+        assert_eq!(IpNumber(13).protocol_str(), Some("ARGUS"));
+        assert_eq!(IpNumber(14).protocol_str(), Some("EMCON"));
+        assert_eq!(IpNumber(15).protocol_str(), Some("Cross Net Debugger"));
+        assert_eq!(IpNumber(16).protocol_str(), Some("Chaos"));
+        assert_eq!(IpNumber(17).protocol_str(), Some("User Datagram"));
+        assert_eq!(IpNumber(18).protocol_str(), Some("Multiplexing"));
+        assert_eq!(
+            IpNumber(19).protocol_str(),
+            Some("DCN Measurement Subsystems")
+        );
+        assert_eq!(IpNumber(20).protocol_str(), Some("Host Monitoring"));
+        assert_eq!(
+            IpNumber(21).protocol_str(),
+            Some("Packet Radio Measurement")
+        );
+        assert_eq!(IpNumber(22).protocol_str(), Some("XEROX NS IDP"));
+        assert_eq!(IpNumber(23).protocol_str(), Some("Trunk-1"));
+        assert_eq!(IpNumber(24).protocol_str(), Some("Trunk-2"));
+        assert_eq!(IpNumber(25).protocol_str(), Some("Leaf-1"));
+        assert_eq!(IpNumber(26).protocol_str(), Some("Leaf-2"));
+        assert_eq!(IpNumber(27).protocol_str(), Some("Reliable Data Protocol"));
+        assert_eq!(
+            IpNumber(28).protocol_str(),
+            Some("Internet Reliable Transaction")
+        );
+        assert_eq!(
+            IpNumber(29).protocol_str(),
+            Some("ISO Transport Protocol Class 4")
+        );
+        assert_eq!(
+            IpNumber(30).protocol_str(),
+            Some("Bulk Data Transfer Protocol")
+        );
+        assert_eq!(
+            IpNumber(31).protocol_str(),
+            Some("MFE Network Services Protocol")
+        );
+        assert_eq!(
+            IpNumber(32).protocol_str(),
+            Some("MERIT Internodal Protocol")
+        );
+        assert_eq!(
+            IpNumber(33).protocol_str(),
+            Some("Datagram Congestion Control Protocol")
+        );
+        assert_eq!(
+            IpNumber(34).protocol_str(),
+            Some("Third Party Connect Protocol")
+        );
+        assert_eq!(
+            IpNumber(35).protocol_str(),
+            Some("Inter-Domain Policy Routing Protocol")
+        );
+        assert_eq!(IpNumber(36).protocol_str(), Some("XTP"));
+        assert_eq!(
+            IpNumber(37).protocol_str(),
+            Some("Datagram Delivery Protocol")
+        );
+        assert_eq!(
+            IpNumber(38).protocol_str(),
+            Some("IDPR Control Message Transport Proto")
+        );
+        assert_eq!(IpNumber(39).protocol_str(), Some("TP++ Transport Protocol"));
+        assert_eq!(IpNumber(40).protocol_str(), Some("IL Transport Protocol"));
+        assert_eq!(IpNumber(41).protocol_str(), Some("IPv6 encapsulation"));
+        assert_eq!(
+            IpNumber(42).protocol_str(),
+            Some("Source Demand Routing Protocol")
+        );
+        assert_eq!(IpNumber(43).protocol_str(), Some("Routing Header for IPv6"));
+        assert_eq!(
+            IpNumber(44).protocol_str(),
+            Some("Fragment Header for IPv6")
+        );
+        assert_eq!(
+            IpNumber(45).protocol_str(),
+            Some("Inter-Domain Routing Protocol")
+        );
+        assert_eq!(IpNumber(46).protocol_str(), Some("Reservation Protocol"));
+        assert_eq!(
+            IpNumber(47).protocol_str(),
+            Some("Generic Routing Encapsulation")
+        );
+        assert_eq!(
+            IpNumber(48).protocol_str(),
+            Some("Dynamic Source Routing Protocol")
+        );
+        assert_eq!(IpNumber(49).protocol_str(), Some("BNA"));
+        assert_eq!(IpNumber(50).protocol_str(), Some("Encap Security Payload"));
+        assert_eq!(IpNumber(51).protocol_str(), Some("Authentication Header"));
+        assert_eq!(
+            IpNumber(52).protocol_str(),
+            Some("Integrated Net Layer Security  TUBA")
+        );
+        assert_eq!(IpNumber(53).protocol_str(), Some("IP with Encryption"));
+        assert_eq!(
+            IpNumber(54).protocol_str(),
+            Some("NBMA Address Resolution Protocol")
+        );
+        assert_eq!(IpNumber(55).protocol_str(), Some("IP Mobility"));
+        assert_eq!(
+            IpNumber(56).protocol_str(),
+            Some("Transport Layer Security Protocol using Kryptonet key management")
+        );
+        assert_eq!(IpNumber(57).protocol_str(), Some("SKIP"));
+        assert_eq!(IpNumber(58).protocol_str(), Some("ICMP for IPv6"));
+        assert_eq!(IpNumber(59).protocol_str(), Some("No Next Header for IPv6"));
+        assert_eq!(
+            IpNumber(60).protocol_str(),
+            Some("Destination Options for IPv6")
+        );
+        assert_eq!(
+            IpNumber(61).protocol_str(),
+            Some("any host internal protocol")
+        );
+        assert_eq!(IpNumber(62).protocol_str(), Some("CFTP"));
+        assert_eq!(IpNumber(63).protocol_str(), Some("any local network"));
+        assert_eq!(
+            IpNumber(64).protocol_str(),
+            Some("SATNET and Backroom EXPAK")
+        );
+        assert_eq!(IpNumber(65).protocol_str(), Some("Kryptolan"));
+        assert_eq!(
+            IpNumber(66).protocol_str(),
+            Some("MIT Remote Virtual Disk Protocol")
+        );
+        assert_eq!(
+            IpNumber(67).protocol_str(),
+            Some("Internet Pluribus Packet Core")
+        );
+        assert_eq!(
+            IpNumber(68).protocol_str(),
+            Some("any distributed file system")
+        );
+        assert_eq!(IpNumber(69).protocol_str(), Some("SATNET Monitoring"));
+        assert_eq!(IpNumber(70).protocol_str(), Some("VISA Protocol"));
+        assert_eq!(
+            IpNumber(71).protocol_str(),
+            Some("Internet Packet Core Utility")
+        );
+        assert_eq!(
+            IpNumber(72).protocol_str(),
+            Some("Computer Protocol Network Executive")
+        );
+        assert_eq!(
+            IpNumber(73).protocol_str(),
+            Some("Computer Protocol Heart Beat")
+        );
+        assert_eq!(IpNumber(74).protocol_str(), Some("Wang Span Network"));
+        assert_eq!(IpNumber(75).protocol_str(), Some("Packet Video Protocol"));
+        assert_eq!(
+            IpNumber(76).protocol_str(),
+            Some("Backroom SATNET Monitoring")
+        );
+        assert_eq!(
+            IpNumber(77).protocol_str(),
+            Some("SUN ND PROTOCOL-Temporary")
+        );
+        assert_eq!(IpNumber(78).protocol_str(), Some("WIDEBAND Monitoring"));
+        assert_eq!(IpNumber(79).protocol_str(), Some("WIDEBAND EXPAK"));
+        assert_eq!(IpNumber(80).protocol_str(), Some("ISO Internet Protocol"));
+        assert_eq!(IpNumber(81).protocol_str(), Some("VMTP"));
+        assert_eq!(IpNumber(82).protocol_str(), Some("SECURE-VMTP"));
+        assert_eq!(IpNumber(83).protocol_str(), Some("VINES"));
+        assert_eq!(
+            IpNumber(84).protocol_str(),
+            Some("Internet Protocol Traffic Manager")
+        );
+        assert_eq!(IpNumber(85).protocol_str(), Some("NSFNET-IGP"));
+        assert_eq!(
+            IpNumber(86).protocol_str(),
+            Some("Dissimilar Gateway Protocol")
+        );
+        assert_eq!(IpNumber(87).protocol_str(), Some("TCF"));
+        assert_eq!(IpNumber(88).protocol_str(), Some("EIGRP"));
+        assert_eq!(IpNumber(89).protocol_str(), Some("OSPFIGP"));
+        assert_eq!(IpNumber(90).protocol_str(), Some("Sprite RPC Protocol"));
+        assert_eq!(
+            IpNumber(91).protocol_str(),
+            Some("Locus Address Resolution Protocol")
+        );
+        assert_eq!(
+            IpNumber(92).protocol_str(),
+            Some("Multicast Transport Protocol")
+        );
+        assert_eq!(IpNumber(93).protocol_str(), Some("AX.25 Frames"));
+        assert_eq!(
+            IpNumber(94).protocol_str(),
+            Some("IP-within-IP Encapsulation Protocol")
+        );
+        assert_eq!(
+            IpNumber(95).protocol_str(),
+            Some("Mobile Internetworking Control Pro.")
+        );
+        assert_eq!(
+            IpNumber(96).protocol_str(),
+            Some("Semaphore Communications Sec. Pro.")
+        );
+        assert_eq!(
+            IpNumber(97).protocol_str(),
+            Some("Ethernet-within-IP Encapsulation")
+        );
+        assert_eq!(IpNumber(98).protocol_str(), Some("Encapsulation Header"));
+        assert_eq!(
+            IpNumber(99).protocol_str(),
+            Some("any private encryption scheme")
+        );
+        assert_eq!(IpNumber(100).protocol_str(), Some("GMTP"));
+        assert_eq!(
+            IpNumber(101).protocol_str(),
+            Some("Ipsilon Flow Management Protocol")
+        );
+        assert_eq!(IpNumber(102).protocol_str(), Some("PNNI over IP"));
+        assert_eq!(
+            IpNumber(103).protocol_str(),
+            Some("Protocol Independent Multicast")
+        );
+        assert_eq!(IpNumber(104).protocol_str(), Some("ARIS"));
+        assert_eq!(IpNumber(105).protocol_str(), Some("SCPS"));
+        assert_eq!(IpNumber(106).protocol_str(), Some("QNX"));
+        assert_eq!(IpNumber(107).protocol_str(), Some("Active Networks"));
+        assert_eq!(
+            IpNumber(108).protocol_str(),
+            Some("IP Payload Compression Protocol")
+        );
+        assert_eq!(
+            IpNumber(109).protocol_str(),
+            Some("Sitara Networks Protocol")
+        );
+        assert_eq!(IpNumber(110).protocol_str(), Some("Compaq Peer Protocol"));
+        assert_eq!(IpNumber(111).protocol_str(), Some("IPX in IP"));
+        assert_eq!(
+            IpNumber(112).protocol_str(),
+            Some("Virtual Router Redundancy Protocol")
+        );
+        assert_eq!(
+            IpNumber(113).protocol_str(),
+            Some("PGM Reliable Transport Protocol")
+        );
+        assert_eq!(IpNumber(114).protocol_str(), Some("any 0-hop protocol"));
+        assert_eq!(
+            IpNumber(115).protocol_str(),
+            Some("Layer Two Tunneling Protocol")
+        );
+        assert_eq!(
+            IpNumber(116).protocol_str(),
+            Some("D-II Data Exchange (DDX)")
+        );
+        assert_eq!(
+            IpNumber(117).protocol_str(),
+            Some("Interactive Agent Transfer Protocol")
+        );
+        assert_eq!(
+            IpNumber(118).protocol_str(),
+            Some("Schedule Transfer Protocol")
+        );
+        assert_eq!(
+            IpNumber(119).protocol_str(),
+            Some("SpectraLink Radio Protocol")
+        );
+        assert_eq!(IpNumber(120).protocol_str(), Some("UTI"));
+        assert_eq!(
+            IpNumber(121).protocol_str(),
+            Some("Simple Message Protocol")
+        );
+        assert_eq!(
+            IpNumber(122).protocol_str(),
+            Some("Simple Multicast Protocol")
+        );
+        assert_eq!(
+            IpNumber(123).protocol_str(),
+            Some("Performance Transparency Protocol")
+        );
+        assert_eq!(IpNumber(124).protocol_str(), None);
+        assert_eq!(IpNumber(125).protocol_str(), None);
+        assert_eq!(
+            IpNumber(126).protocol_str(),
+            Some("Combat Radio Transport Protocol")
+        );
+        assert_eq!(
+            IpNumber(127).protocol_str(),
+            Some("Combat Radio User Datagram")
+        );
+        assert_eq!(IpNumber(128).protocol_str(), None);
+        assert_eq!(IpNumber(129).protocol_str(), None);
+        assert_eq!(IpNumber(130).protocol_str(), Some("Secure Packet Shield"));
+        assert_eq!(
+            IpNumber(131).protocol_str(),
+            Some("Private IP Encapsulation within IP")
+        );
+        assert_eq!(
+            IpNumber(132).protocol_str(),
+            Some("Stream Control Transmission Protocol")
+        );
+        assert_eq!(IpNumber(133).protocol_str(), Some("Fibre Channel"));
+        assert_eq!(IpNumber(134).protocol_str(), None);
+        assert_eq!(IpNumber(135).protocol_str(), None);
+        assert_eq!(IpNumber(136).protocol_str(), None);
+        assert_eq!(IpNumber(137).protocol_str(), None);
+        assert_eq!(IpNumber(138).protocol_str(), Some("MANET Protocols"));
+        assert_eq!(IpNumber(139).protocol_str(), Some("Host Identity Protocol"));
+        assert_eq!(IpNumber(140).protocol_str(), Some("Shim6 Protocol"));
+        assert_eq!(
+            IpNumber(141).protocol_str(),
+            Some("Wrapped Encapsulating Security Payload")
+        );
+        assert_eq!(
+            IpNumber(142).protocol_str(),
+            Some("Robust Header Compression")
+        );
+        assert_eq!(IpNumber(143).protocol_str(), Some("Ethernet"));
+        assert_eq!(
+            IpNumber(144).protocol_str(),
+            Some("AGGFRAG encapsulation payload for ESP")
+        );
+        for i in 145u8..=252 {
+            assert_eq!(IpNumber(i).protocol_str(), None);
+        }
+        assert_eq!(
+            IpNumber(253).protocol_str(),
+            Some("Use for experimentation and testing")
+        );
+        assert_eq!(
+            IpNumber(254).protocol_str(),
+            Some("Use for experimentation and testing")
+        );
+        assert_eq!(IpNumber(255).protocol_str(), None);
+    }
+
+    #[test]
+    fn ip_number_eq_check() {
+        use crate::ip_number::*;
+        use crate::IpNumber;
+        let pairs = &[
+            (IPV6_HOP_BY_HOP, IpNumber::IPV6_HEADER_HOP_BY_HOP),
+            (IPV6_HEADER_HOP_BY_HOP, IpNumber::IPV6_HEADER_HOP_BY_HOP),
+            (ICMP, IpNumber::ICMP),
+            (IGMP, IpNumber::IGMP),
+            (GGP, IpNumber::GGP),
+            (IPV4, IpNumber::IPV4),
+            (STREAM, IpNumber::STREAM),
+            (TCP, IpNumber::TCP),
+            (CBT, IpNumber::CBT),
+            (EGP, IpNumber::EGP),
+            (IGP, IpNumber::IGP),
+            (BBN_RCC_MON, IpNumber::BBN_RCC_MON),
+            (NVP_II, IpNumber::NVP_II),
+            (PUP, IpNumber::PUP),
+            (ARGUS, IpNumber::ARGUS),
+            (EMCON, IpNumber::EMCON),
+            (XNET, IpNumber::XNET),
+            (CHAOS, IpNumber::CHAOS),
+            (UDP, IpNumber::UDP),
+            (MUX, IpNumber::MUX),
+            (DCN_MEAS, IpNumber::DCN_MEAS),
+            (HMP, IpNumber::HMP),
+            (PRM, IpNumber::PRM),
+            (XNS_IDP, IpNumber::XNS_IDP),
+            (TRUNK1, IpNumber::TRUNK1),
+            (TRUNK2, IpNumber::TRUNK2),
+            (LEAF1, IpNumber::LEAF1),
+            (LEAF2, IpNumber::LEAF2),
+            (RDP, IpNumber::RDP),
+            (IRTP, IpNumber::IRTP),
+            (ISO_TP4, IpNumber::ISO_TP4),
+            (NET_BLT, IpNumber::NET_BLT),
+            (MFE_NSP, IpNumber::MFE_NSP),
+            (MERIT_INP, IpNumber::MERIT_INP),
+            (DCCP, IpNumber::DCCP),
+            (
+                THIRD_PARTY_CONNECT_PROTOCOL,
+                IpNumber::THIRD_PARTY_CONNECT_PROTOCOL,
+            ),
+            (IDPR, IpNumber::IDPR),
+            (XTP, IpNumber::XTP),
+            (DDP, IpNumber::DDP),
+            (IDPR_CMTP, IpNumber::IDPR_CMTP),
+            (TP_PLUS_PLUS, IpNumber::TP_PLUS_PLUS),
+            (IL, IpNumber::IL),
+            (IPV6, IpNumber::IPV6),
+            (SDRP, IpNumber::SDRP),
+            (IPV6_ROUTE_HEADER, IpNumber::IPV6_ROUTE_HEADER),
+            (IPV6_ROUTE, IpNumber::IPV6_ROUTE_HEADER),
+            (
+                IPV6_FRAGMENTATION_HEADER,
+                IpNumber::IPV6_FRAGMENTATION_HEADER,
+            ),
+            (IPV6_FRAG, IpNumber::IPV6_FRAGMENTATION_HEADER),
+            (IDRP, IpNumber::IDRP),
+            (RSVP, IpNumber::RSVP),
+            (GRE, IpNumber::GRE),
+            (DSR, IpNumber::DSR),
+            (BNA, IpNumber::BNA),
+            (ENCAP_SEC, IpNumber::ENCAPSULATING_SECURITY_PAYLOAD),
+            (
+                ENCAPSULATING_SECURITY_PAYLOAD,
+                IpNumber::ENCAPSULATING_SECURITY_PAYLOAD,
+            ),
+            (AUTH, IpNumber::AUTHENTICATION_HEADER),
+            (AUTHENTICATION_HEADER, IpNumber::AUTHENTICATION_HEADER),
+            (INLSP, IpNumber::INLSP),
+            (SWIPE, IpNumber::SWIPE),
+            (NARP, IpNumber::NARP),
+            (MOBILE, IpNumber::MOBILE),
+            (TLSP, IpNumber::TLSP),
+            (SKIP, IpNumber::SKIP),
+            (IPV6_ICMP, IpNumber::IPV6_ICMP),
+            (IPV6_NO_NEXT_HEADER, IpNumber::IPV6_NO_NEXT_HEADER),
+            (IPV6_DEST_OPTIONS, IpNumber::IPV6_DESTINATION_OPTIONS),
+            (IPV6_DESTINATION_OPTIONS, IpNumber::IPV6_DESTINATION_OPTIONS),
+            (
+                ANY_HOST_INTERNAL_PROTOCOL,
+                IpNumber::ANY_HOST_INTERNAL_PROTOCOL,
+            ),
+            (CFTP, IpNumber::CFTP),
+            (ANY_LOCAL_NETWORK, IpNumber::ANY_LOCAL_NETWORK),
+            (SAT_EXPAK, IpNumber::SAT_EXPAK),
+            (KRYTOLAN, IpNumber::KRYTOLAN),
+            (RVD, IpNumber::RVD),
+            (IPPC, IpNumber::IPPC),
+            (
+                ANY_DISTRIBUTED_FILE_SYSTEM,
+                IpNumber::ANY_DISTRIBUTED_FILE_SYSTEM,
+            ),
+            (SAT_MON, IpNumber::SAT_MON),
+            (VISA, IpNumber::VISA),
+            (IPCV, IpNumber::IPCV),
+            (CPNX, IpNumber::CPNX),
+            (CPHB, IpNumber::CPHB),
+            (WSN, IpNumber::WSN),
+            (PVP, IpNumber::PVP),
+            (BR_SAT_MON, IpNumber::BR_SAT_MON),
+            (SUN_ND, IpNumber::SUN_ND),
+            (WB_MON, IpNumber::WB_MON),
+            (WB_EXPAK, IpNumber::WB_EXPAK),
+            (ISO_IP, IpNumber::ISO_IP),
+            (VMTP, IpNumber::VMTP),
+            (SECURE_VMTP, IpNumber::SECURE_VMTP),
+            (VINES, IpNumber::VINES),
+            (TTP_OR_IPTM, IpNumber::TTP_OR_IPTM),
+            (NSFNET_IGP, IpNumber::NSFNET_IGP),
+            (DGP, IpNumber::DGP),
+            (TCF, IpNumber::TCF),
+            (EIGRP, IpNumber::EIGRP),
+            (OSPFIGP, IpNumber::OSPFIGP),
+            (SPRITE_RPC, IpNumber::SPRITE_RPC),
+            (LARP, IpNumber::LARP),
+            (MTP, IpNumber::MTP),
+            (AX25, IpNumber::AX25),
+            (IPIP, IpNumber::IPIP),
+            (MICP, IpNumber::MICP),
+            (SCC_SP, IpNumber::SCC_SP),
+            (ETHER_IP, IpNumber::ETHER_IP),
+            (ENCAP, IpNumber::ENCAP),
+            (GMTP, IpNumber::GMTP),
+            (IFMP, IpNumber::IFMP),
+            (PNNI, IpNumber::PNNI),
+            (PIM, IpNumber::PIM),
+            (ARIS, IpNumber::ARIS),
+            (SCPS, IpNumber::SCPS),
+            (QNX, IpNumber::QNX),
+            (ACTIVE_NETWORKS, IpNumber::ACTIVE_NETWORKS),
+            (IP_COMP, IpNumber::IP_COMP),
+            (SITRA_NETWORKS_PROTOCOL, IpNumber::SITRA_NETWORKS_PROTOCOL),
+            (COMPAQ_PEER, IpNumber::COMPAQ_PEER),
+            (IPX_IN_IP, IpNumber::IPX_IN_IP),
+            (VRRP, IpNumber::VRRP),
+            (PGM, IpNumber::PGM),
+            (ANY_ZERO_HOP_PROTOCOL, IpNumber::ANY_ZERO_HOP_PROTOCOL),
+            (
+                LAYER2_TUNNELING_PROTOCOL,
+                IpNumber::LAYER2_TUNNELING_PROTOCOL,
+            ),
+            (DDX, IpNumber::DDX),
+            (IATP, IpNumber::IATP),
+            (STP, IpNumber::STP),
+            (SRP, IpNumber::SRP),
+            (UTI, IpNumber::UTI),
+            (SIMPLE_MESSAGE_PROTOCOL, IpNumber::SIMPLE_MESSAGE_PROTOCOL),
+            (SM, IpNumber::SM),
+            (PTP, IpNumber::PTP),
+            (ISIS_OVER_IPV4, IpNumber::ISIS_OVER_IPV4),
+            (FIRE, IpNumber::FIRE),
+            (CRTP, IpNumber::CRTP),
+            (CRUDP, IpNumber::CRUDP),
+            (SSCOPMCE, IpNumber::SSCOPMCE),
+            (IPLT, IpNumber::IPLT),
+            (SPS, IpNumber::SPS),
+            (PIPE, IpNumber::PIPE),
+            (SCTP, IpNumber::SCTP),
+            (FC, IpNumber::FC),
+            (RSVP_E2E_IGNORE, IpNumber::RSVP_E2E_IGNORE),
+            (MOBILITY, IpNumber::MOBILITY_HEADER),
+            (MOBILITY_HEADER, IpNumber::MOBILITY_HEADER),
+            (UDP_LITE, IpNumber::UDP_LITE),
+            (MPLS_IN_IP, IpNumber::MPLS_IN_IP),
+            (MANET, IpNumber::MANET),
+            (HIP, IpNumber::HIP),
+            (SHIM6, IpNumber::SHIM6),
+            (WESP, IpNumber::WESP),
+            (ROHC, IpNumber::ROHC),
+            (EXP0, IpNumber::EXPERIMENTAL_AND_TESTING_0),
+            (
+                EXPERIMENTAL_AND_TESTING_0,
+                IpNumber::EXPERIMENTAL_AND_TESTING_0,
+            ),
+            (EXP1, IpNumber::EXPERIMENTAL_AND_TESTING_1),
+            (
+                EXPERIMENTAL_AND_TESTING_1,
+                IpNumber::EXPERIMENTAL_AND_TESTING_1,
+            ),
+        ];
+        for (raw, enum_value) in pairs {
+            assert_eq!(*raw, *enum_value);
+        }
+    }
+
+    #[test]
+    fn default() {
+        let actual: IpNumber = Default::default();
+        assert_eq!(actual, IpNumber(255));
+    }
+
+    proptest! {
+        #[test]
+        fn into(num in any::<u8>()) {
+            {
+                let converted: u8 = IpNumber(num).into();
+                assert_eq!(converted, num);
+            }
+            {
+                let converted: IpNumber = num.into();
+                assert_eq!(converted, IpNumber(num));
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from(num in any::<u8>()) {
+            {
+                let converted: u8 = u8::from(IpNumber(num));
+                assert_eq!(converted, num);
+            }
+            {
+                let converted: IpNumber = IpNumber::from(num);
+                assert_eq!(converted, IpNumber(num));
+            }
+        }
+    }
+
+    #[test]
+    fn debug() {
+        // keyword & protocol string exist
+        assert_eq!(
+            format!("{:?}", IpNumber::UDP),
+            format!("17 (UDP - User Datagram)")
+        );
+        // only keyword string exist
+        assert_eq!(
+            format!("{:?}", IpNumber::MOBILITY_HEADER),
+            format!("135 (Mobility Header)")
+        );
+        // only protocol string exist
+        assert_eq!(
+            format!("{:?}", IpNumber(253)),
+            format!("253 (Use for experimentation and testing)")
+        );
+        // no keyword & no protocol string
+        assert_eq!(format!("{:?}", IpNumber(145)), format!("145"));
+    }
+
+    #[test]
+    fn clone_eq_hash_ord() {
+        // clone eq
+        let value = IpNumber::IPV6_HEADER_HOP_BY_HOP;
+        assert_eq!(value, value.clone());
+        // hash
+        let a_hash = {
+            let mut s = DefaultHasher::new();
+            value.hash(&mut s);
+            s.finish()
+        };
+        let b_hash = {
+            let mut s = DefaultHasher::new();
+            value.hash(&mut s);
+            s.finish()
+        };
+        assert_eq!(a_hash, b_hash);
+        // order
+        assert_eq!(value.cmp(&value.clone()), Ordering::Equal);
+        assert!(value.ge(&value.clone()));
+    }
+}
diff --git a/src/net/ip_payload_slice.rs b/src/net/ip_payload_slice.rs
new file mode 100644
index 0000000..91456e9
--- /dev/null
+++ b/src/net/ip_payload_slice.rs
@@ -0,0 +1,78 @@
+use crate::*;
+
+/// Payload of an IP packet.
+#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
+pub struct IpPayloadSlice<'a> {
+    /// Identifying content of the payload.
+    pub ip_number: IpNumber,
+
+    /// True if the payload is not complete and has been fragmented.
+    ///
+    /// This can occur if the IPv4 incdicates that the payload
+    /// has been fragmented or if there is an IPv6 fragmentation
+    /// header indicating that the payload has been fragmented.
+    pub fragmented: bool,
+
+    /// Length field that was used to determine the length
+    /// of the payload (e.g. IPv6 "payload_length" field).
+    pub len_source: LenSource,
+
+    /// Payload
+    pub payload: &'a [u8],
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        let s = IpPayloadSlice {
+            ip_number: IpNumber::UDP,
+            fragmented: true,
+            len_source: LenSource::Slice,
+            payload: &[],
+        };
+        assert_eq!(
+            format!(
+                "IpPayloadSlice {{ ip_number: {:?}, fragmented: {:?}, len_source: {:?}, payload: {:?} }}",
+                s.ip_number,
+                s.fragmented,
+                s.len_source,
+                s.payload
+            ),
+            format!("{:?}", s)
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash_ord() {
+        let s = IpPayloadSlice {
+            ip_number: IpNumber::UDP,
+            fragmented: true,
+            len_source: LenSource::Slice,
+            payload: &[],
+        };
+        assert_eq!(s.clone(), s);
+
+        use std::collections::hash_map::DefaultHasher;
+        use std::hash::{Hash, Hasher};
+
+        let a_hash = {
+            let mut hasher = DefaultHasher::new();
+            s.hash(&mut hasher);
+            hasher.finish()
+        };
+        let b_hash = {
+            let mut hasher = DefaultHasher::new();
+            s.clone().hash(&mut hasher);
+            hasher.finish()
+        };
+        assert_eq!(a_hash, b_hash);
+
+        use std::cmp::Ordering;
+        assert_eq!(s.clone().cmp(&s), Ordering::Equal);
+        assert_eq!(s.clone().partial_cmp(&s), Some(Ordering::Equal));
+    }
+}
diff --git a/src/net/ip_slice.rs b/src/net/ip_slice.rs
new file mode 100644
index 0000000..6203d74
--- /dev/null
+++ b/src/net/ip_slice.rs
@@ -0,0 +1,750 @@
+use crate::{
+    err::{ip, Layer, LenError},
+    *,
+};
+
+/// Slice containing the IP header (v4 or v6), extension headers &
+/// payload.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum IpSlice<'a> {
+    /// The ipv4 header & the decoded extension headers.
+    Ipv4(Ipv4Slice<'a>),
+    /// The ipv6 header & the decoded extension headers.
+    Ipv6(Ipv6Slice<'a>),
+}
+
+impl<'a> IpSlice<'a> {
+    /// Returns a reference to the `Ipv4Slice` if `self` is a `IpSlice::Ipv4`.
+    pub fn ipv4(&self) -> Option<&Ipv4Slice> {
+        use IpSlice::*;
+        match self {
+            Ipv4(slice) => Some(slice),
+            Ipv6(_) => None,
+        }
+    }
+
+    /// Returns a reference to the `Ipv6Slice` if `self` is a `IpSlice::Ipv6`.
+    pub fn ipv6(&self) -> Option<&Ipv6Slice> {
+        use IpSlice::*;
+        match self {
+            Ipv4(_) => None,
+            Ipv6(slice) => Some(slice),
+        }
+    }
+
+    /// Returns true if the payload is fragmented.
+    pub fn is_fragmenting_payload(&self) -> bool {
+        match self {
+            IpSlice::Ipv4(s) => s.is_payload_fragmented(),
+            IpSlice::Ipv6(s) => s.is_payload_fragmented(),
+        }
+    }
+
+    /// Return the source address as an std::net::Ipvddr (requires
+    /// crate feature `std`).
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn source_addr(&self) -> std::net::IpAddr {
+        match self {
+            IpSlice::Ipv4(s) => s.header().source_addr().into(),
+            IpSlice::Ipv6(s) => s.header().source_addr().into(),
+        }
+    }
+
+    /// Return the destination address as an std::net::IpAddr (requires
+    /// crate feature `std`).
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn destination_addr(&self) -> std::net::IpAddr {
+        match self {
+            IpSlice::Ipv4(s) => s.header().destination_addr().into(),
+            IpSlice::Ipv6(s) => s.header().destination_addr().into(),
+        }
+    }
+
+    /// Returns a slice containing the data after the IP header
+    /// and IP extensions headers.
+    #[inline]
+    pub fn payload(&self) -> &IpPayloadSlice<'a> {
+        use IpSlice::*;
+        match self {
+            Ipv4(ipv4) => ipv4.payload(),
+            Ipv6(ipv6) => ipv6.payload(),
+        }
+    }
+
+    /// Returns the ip number the type of payload of the IP packet.
+    ///
+    /// This function returns the ip number stored in the last
+    /// IP header or extension header.
+    #[inline]
+    pub fn payload_ip_number(&self) -> IpNumber {
+        use IpSlice::*;
+        match self {
+            Ipv4(ipv4) => ipv4.payload().ip_number,
+            Ipv6(ipv6) => ipv6.payload().ip_number,
+        }
+    }
+
+    /// Separates and validates IP headers (including extension headers)
+    /// in the given slice and determine the sub-slice containing the payload
+    /// of the IP packet.
+    pub fn from_slice(slice: &[u8]) -> Result<IpSlice, err::ip::SliceError> {
+        use crate::ip_number::AUTH;
+        use err::ip::{HeaderError::*, HeadersError::*, SliceError::*};
+        use IpSlice::*;
+
+        if slice.is_empty() {
+            Err(Len(err::LenError {
+                required_len: 1,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::IpHeader,
+                layer_start_offset: 0,
+            }))
+        } else {
+            // SAFETY: Safe as slice is not empty.
+            let first_byte = unsafe { slice.get_unchecked(0) };
+            match first_byte >> 4 {
+                4 => {
+                    let ihl = first_byte & 0xf;
+
+                    // check that the ihl has at least the length of the base IPv4 header
+                    if ihl < 5 {
+                        return Err(IpHeaders(Ip(Ipv4HeaderLengthSmallerThanHeader { ihl })));
+                    }
+
+                    // check there is enough data for the header
+                    let header_len = (usize::from(ihl)) * 4;
+                    if slice.len() < header_len {
+                        return Err(Len(err::LenError {
+                            required_len: header_len,
+                            len: slice.len(),
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv4Header,
+                            layer_start_offset: 0,
+                        }));
+                    }
+
+                    // SAFETY:
+                    // Safe as the slice length is checked to be at least
+                    // header_len or greater above.
+                    let header = unsafe {
+                        Ipv4HeaderSlice::from_slice_unchecked(core::slice::from_raw_parts(
+                            slice.as_ptr(),
+                            header_len,
+                        ))
+                    };
+
+                    // check the total_length at least contains the header
+                    let total_len = usize::from(header.total_len());
+                    if total_len < header_len {
+                        return Err(Len(LenError {
+                            required_len: header_len,
+                            len: total_len,
+                            len_source: LenSource::Ipv4HeaderTotalLen,
+                            layer: Layer::Ipv4Packet,
+                            layer_start_offset: 0,
+                        }));
+                    }
+
+                    // validate the total length against the slice
+                    let header_payload = if slice.len() < total_len {
+                        return Err(Len(LenError {
+                            required_len: total_len,
+                            len: slice.len(),
+                            len_source: LenSource::Slice,
+                            layer: Layer::Ipv4Packet,
+                            layer_start_offset: 0,
+                        }));
+                    } else {
+                        unsafe {
+                            core::slice::from_raw_parts(
+                                // 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 verified in an
+                                // if statement above as well as that slice.len() >= total_length_usize.
+                                total_len - header_len,
+                            )
+                        }
+                    };
+
+                    // slice extension headers
+                    // decode the authentication header if needed
+                    let fragmented = header.is_fragmenting_payload();
+                    match header.protocol() {
+                        AUTH => {
+                            use crate::err::ip_auth::HeaderSliceError as E;
+
+                            // parse extension headers
+                            let auth = match IpAuthHeaderSlice::from_slice(header_payload) {
+                                Ok(s) => s,
+                                Err(err) => match err {
+                                    E::Len(mut l) => {
+                                        // change the length source to the ipv4 header
+                                        l.len_source = LenSource::Ipv4HeaderTotalLen;
+                                        l.layer_start_offset += header.slice().len();
+                                        return Err(Len(l));
+                                    }
+                                    E::Content(err) => {
+                                        return Err(IpHeaders(ip::HeadersError::Ipv4Ext(err)))
+                                    }
+                                },
+                            };
+
+                            // remove the extension header from the payload
+                            let payload = unsafe {
+                                core::slice::from_raw_parts(
+                                    header_payload.as_ptr().add(auth.slice().len()),
+                                    header_payload.len() - auth.slice().len(),
+                                )
+                            };
+                            Ok(Ipv4(Ipv4Slice {
+                                header,
+                                exts: Ipv4ExtensionsSlice { auth: Some(auth) },
+                                payload: IpPayloadSlice {
+                                    ip_number: auth.next_header(),
+                                    fragmented,
+                                    len_source: LenSource::Ipv4HeaderTotalLen,
+                                    payload,
+                                },
+                            }))
+                        }
+                        ip_number => Ok(Ipv4(Ipv4Slice {
+                            header,
+                            exts: Ipv4ExtensionsSlice { auth: None },
+                            payload: IpPayloadSlice {
+                                ip_number,
+                                fragmented,
+                                len_source: LenSource::Ipv4HeaderTotalLen,
+                                payload: header_payload,
+                            },
+                        })),
+                    }
+                }
+                6 => {
+                    // check length
+                    if slice.len() < Ipv6Header::LEN {
+                        return Err(Len(err::LenError {
+                            required_len: Ipv6Header::LEN,
+                            len: slice.len(),
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv6Header,
+                            layer_start_offset: 0,
+                        }));
+                    }
+
+                    let header = unsafe {
+                        Ipv6HeaderSlice::from_slice_unchecked(core::slice::from_raw_parts(
+                            slice.as_ptr(),
+                            Ipv6Header::LEN,
+                        ))
+                    };
+
+                    // restrict slice by the length specified in the header
+                    let (header_payload, len_source) =
+                        if 0 == header.payload_length() && slice.len() > Ipv6Header::LEN {
+                            // In case the payload_length is 0 assume that the entire
+                            // rest of the slice is part of the packet until the jumbogram
+                            // parameters can be parsed.
+
+                            // TODO: Add payload length parsing from the jumbogram
+                            (
+                                unsafe {
+                                    core::slice::from_raw_parts(
+                                        slice.as_ptr().add(Ipv6Header::LEN),
+                                        slice.len() - Ipv6Header::LEN,
+                                    )
+                                },
+                                LenSource::Slice,
+                            )
+                        } else {
+                            let payload_len = usize::from(header.payload_length());
+                            let expected_len = Ipv6Header::LEN + payload_len;
+                            if slice.len() < expected_len {
+                                return Err(Len(LenError {
+                                    required_len: expected_len,
+                                    len: slice.len(),
+                                    len_source: LenSource::Slice,
+                                    layer: Layer::Ipv6Packet,
+                                    layer_start_offset: 0,
+                                }));
+                            } else {
+                                (
+                                    unsafe {
+                                        core::slice::from_raw_parts(
+                                            slice.as_ptr().add(Ipv6Header::LEN),
+                                            payload_len,
+                                        )
+                                    },
+                                    LenSource::Ipv6HeaderPayloadLen,
+                                )
+                            }
+                        };
+
+                    // parse extension headers
+                    let (exts, payload_ip_number, payload) =
+                        Ipv6ExtensionsSlice::from_slice(header.next_header(), header_payload)
+                            .map_err(|err| {
+                                // modify length errors
+                                use crate::err::ipv6_exts::HeaderSliceError as I;
+                                match err {
+                                    I::Len(mut err) => {
+                                        err.len_source = LenSource::Ipv6HeaderPayloadLen;
+                                        err.layer_start_offset += Ipv6Header::LEN;
+                                        Len(err)
+                                    }
+                                    I::Content(err) => IpHeaders(ip::HeadersError::Ipv6Ext(err)),
+                                }
+                            })?;
+
+                    let fragmented = exts.is_fragmenting_payload();
+                    Ok(Ipv6(Ipv6Slice {
+                        header,
+                        exts,
+                        payload: IpPayloadSlice {
+                            ip_number: payload_ip_number,
+                            fragmented,
+                            len_source,
+                            payload,
+                        },
+                    }))
+                }
+                version_number => Err(IpHeaders(Ip(UnsupportedIpVersion { version_number }))),
+            }
+        }
+    }
+}
+
+impl<'a> From<Ipv4Slice<'a>> for IpSlice<'a> {
+    fn from(value: Ipv4Slice<'a>) -> Self {
+        IpSlice::Ipv4(value)
+    }
+}
+
+impl<'a> From<Ipv6Slice<'a>> for IpSlice<'a> {
+    fn from(value: Ipv6Slice<'a>) -> Self {
+        IpSlice::Ipv6(value)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+
+    #[test]
+    fn debug_clone_eq() {
+        // ipv4
+        {
+            let mut header: Ipv4Header = Default::default();
+            header.protocol = ip_number::UDP;
+            header.set_payload_len(0).unwrap();
+            let buffer = header.to_bytes();
+
+            let ipv4 = Ipv4Slice::from_slice(&buffer).unwrap();
+            let slice = IpSlice::Ipv4(ipv4.clone());
+
+            // clone & eq
+            assert_eq!(slice.clone(), slice);
+
+            // debug
+            assert_eq!(format!("{:?}", slice), format!("Ipv4({:?})", ipv4));
+        }
+        // ipv6
+        {
+            let header = Ipv6Header {
+                payload_length: 0,
+                next_header: ip_number::UDP,
+                ..Default::default()
+            };
+            let buffer = header.to_bytes();
+            let ipv6 = Ipv6Slice::from_slice(&buffer).unwrap();
+            let slice = IpSlice::Ipv6(ipv6.clone());
+
+            // clone & eq
+            assert_eq!(slice.clone(), slice);
+
+            // debug
+            assert_eq!(format!("{:?}", slice), format!("Ipv6({:?})", ipv6));
+        }
+    }
+
+    #[test]
+    fn is_fragmenting_payload() {
+        for fragment in [false, true] {
+            use ip_number::UDP;
+            // ipv4
+            {
+                let mut ipv4 = Ipv4Header::new(0, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10]).unwrap();
+                if fragment {
+                    ipv4.fragment_offset = 123.try_into().unwrap();
+                }
+
+                let data = ipv4.to_bytes();
+                let ipv4_slice = Ipv4Slice::from_slice(&data).unwrap();
+                assert_eq!(fragment, IpSlice::Ipv4(ipv4_slice).is_fragmenting_payload());
+            }
+
+            // ipv6
+            {
+                let ipv6_frag = Ipv6FragmentHeader {
+                    next_header: UDP,
+                    fragment_offset: IpFragOffset::ZERO,
+                    more_fragments: fragment,
+                    identification: 0,
+                };
+                let ipv6 = Ipv6Header {
+                    traffic_class: 0,
+                    flow_label: 1.try_into().unwrap(),
+                    payload_length: ipv6_frag.header_len() as u16,
+                    next_header: ip_number::IPV6_FRAG,
+                    hop_limit: 4,
+                    source: [1; 16],
+                    destination: [2; 16],
+                };
+                let mut data = Vec::with_capacity(ipv6.header_len() + ipv6_frag.header_len());
+                data.extend_from_slice(&ipv6.to_bytes());
+                data.extend_from_slice(&ipv6_frag.to_bytes());
+
+                assert_eq!(
+                    fragment,
+                    IpSlice::Ipv6(Ipv6Slice::from_slice(&data).unwrap()).is_fragmenting_payload()
+                );
+            }
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source_addr() {
+        // ipv4
+        {
+            let data = Ipv4Header::new(0, 1, 2.into(), [3, 4, 5, 6], [7, 8, 9, 10])
+                .unwrap()
+                .to_bytes();
+            assert_eq!(
+                IpAddr::V4(Ipv4Addr::from([3, 4, 5, 6])),
+                IpSlice::Ipv4(Ipv4Slice::from_slice(&data[..]).unwrap()).source_addr()
+            );
+        }
+
+        // ipv6
+        {
+            let data = Ipv6Header {
+                traffic_class: 0,
+                flow_label: 1.try_into().unwrap(),
+                payload_length: 0,
+                next_header: ip_number::IGMP,
+                hop_limit: 4,
+                source: [1; 16],
+                destination: [2; 16],
+            }
+            .to_bytes();
+
+            assert_eq!(
+                IpAddr::V6(Ipv6Addr::from([1; 16])),
+                IpSlice::Ipv6(Ipv6Slice::from_slice(&data[..]).unwrap()).source_addr()
+            );
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn destination_addr() {
+        use crate::ip_number::UDP;
+
+        // ipv4
+        {
+            let data = Ipv4Header::new(0, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10])
+                .unwrap()
+                .to_bytes();
+
+            assert_eq!(
+                IpAddr::V4(Ipv4Addr::from([7, 8, 9, 10])),
+                IpSlice::Ipv4(Ipv4Slice::from_slice(&data[..]).unwrap()).destination_addr()
+            );
+        }
+
+        // ipv6
+        {
+            let data = Ipv6Header {
+                traffic_class: 0,
+                flow_label: 1.try_into().unwrap(),
+                payload_length: 0,
+                next_header: ip_number::IGMP,
+                hop_limit: 4,
+                source: [1; 16],
+                destination: [2; 16],
+            }
+            .to_bytes();
+
+            assert_eq!(
+                IpAddr::V6(Ipv6Addr::from([2; 16])),
+                IpSlice::Ipv6(Ipv6Slice::from_slice(&data).unwrap()).destination_addr()
+            );
+        }
+    }
+
+    #[test]
+    fn payload() {
+        let payload: [u8; 4] = [1, 2, 3, 4];
+        // ipv4
+        {
+            let header = Ipv4Header::new(
+                payload.len() as u16,
+                1,
+                ip_number::UDP,
+                [3, 4, 5, 6],
+                [7, 8, 9, 10],
+            )
+            .unwrap();
+            let mut data = Vec::with_capacity(header.header_len() + payload.len());
+            data.extend_from_slice(&header.to_bytes());
+            data.extend_from_slice(&payload);
+            assert_eq!(
+                IpSlice::Ipv4(Ipv4Slice::from_slice(&data[..]).unwrap()).payload(),
+                &IpPayloadSlice {
+                    ip_number: ip_number::UDP.into(),
+                    fragmented: header.is_fragmenting_payload(),
+                    len_source: LenSource::Ipv4HeaderTotalLen,
+                    payload: &payload,
+                }
+            );
+        }
+
+        // ipv6
+        {
+            let header = Ipv6Header {
+                traffic_class: 0,
+                flow_label: 1.try_into().unwrap(),
+                payload_length: payload.len() as u16,
+                next_header: ip_number::UDP,
+                hop_limit: 4,
+                source: [1; 16],
+                destination: [2; 16],
+            };
+            let mut data = Vec::with_capacity(header.header_len() + payload.len());
+            data.extend_from_slice(&header.to_bytes());
+            data.extend_from_slice(&payload);
+            assert_eq!(
+                IpSlice::Ipv6(Ipv6Slice::from_slice(&data[..]).unwrap()).payload(),
+                &IpPayloadSlice {
+                    ip_number: ip_number::UDP.into(),
+                    fragmented: false,
+                    len_source: LenSource::Ipv6HeaderPayloadLen,
+                    payload: &payload,
+                }
+            );
+        }
+    }
+
+    #[test]
+    fn payload_ip_number() {
+        use crate::ip_number::{IGMP, UDP};
+
+        // ipv4
+        {
+            let data = Ipv4Header::new(0, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10])
+                .unwrap()
+                .to_bytes();
+            assert_eq!(
+                UDP,
+                IpSlice::Ipv4(Ipv4Slice::from_slice(&data[..]).unwrap()).payload_ip_number()
+            );
+        }
+
+        // ipv6
+        {
+            let data = Ipv6Header {
+                traffic_class: 0,
+                flow_label: 1.try_into().unwrap(),
+                payload_length: 0,
+                next_header: IGMP,
+                hop_limit: 4,
+                source: [1; 16],
+                destination: [2; 16],
+            }
+            .to_bytes();
+
+            assert_eq!(
+                IGMP,
+                IpSlice::Ipv6(Ipv6Slice::from_slice(&data).unwrap()).payload_ip_number()
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_ip_slice(
+            ipv4_header in ipv4_any(),
+            ipv4_exts in ipv4_extensions_with(ip_number::UDP),
+            ipv6_header in ipv6_any(),
+            mut ipv6_exts in ipv6_extensions_with(ip_number::UDP)
+        ) {
+            let payload = [1,2,3,4];
+
+            // setup header length & fields
+            let ipv4_header = {
+                let mut header = ipv4_header;
+                header.protocol = if ipv4_exts.auth.is_some() {
+                    ip_number::AUTH
+                } else {
+                    ip_number::UDP
+                };
+                header.total_len = (header.header_len() + ipv4_exts.header_len() + payload.len()) as u16;
+                header.header_checksum = header.calc_header_checksum();
+                header
+            };
+
+            let ipv4 = IpHeaders::Ipv4(
+                ipv4_header.clone(),
+                ipv4_exts.clone()
+            );
+
+            let ipv6_header = {
+                let mut header = ipv6_header;
+                header.next_header = ipv6_exts.set_next_headers(ip_number::UDP);
+                header.payload_length = (ipv6_exts.header_len() + payload.len()) as u16;
+                header
+            };
+
+            let ipv6 = IpHeaders::Ipv6(
+                ipv6_header.clone(),
+                ipv6_exts.clone()
+            );
+
+            // happy path v4
+            {
+                // build packet
+                let mut data = Vec::with_capacity(ipv4.header_len() + payload.len());
+                ipv4.write(&mut data).unwrap();
+                data.extend_from_slice(&payload);
+
+                // run test
+                let actual = IpSlice::from_slice(&data).unwrap();
+                assert!(actual.ipv6().is_none());
+                let actual = actual.ipv4().unwrap().clone();
+                assert_eq!(actual.header.to_header(), ipv4_header);
+                assert_eq!(actual.extensions().to_header(), ipv4_exts);
+                assert_eq!(
+                    actual.payload,
+                    IpPayloadSlice{
+                        ip_number: ip_number::UDP.into(),
+                        fragmented: ipv4_header.is_fragmenting_payload(),
+                        len_source: LenSource::Ipv4HeaderTotalLen,
+                        payload: &payload
+                    }
+                );
+            }
+
+            // happy path v6
+            {
+                // build packet
+                let mut data = Vec::with_capacity(ipv6.header_len() + payload.len());
+                ipv6.write(&mut data).unwrap();
+                data.extend_from_slice(&payload);
+
+                // run test
+                let actual = crate::IpSlice::from_slice(&data).unwrap();
+                assert!(actual.ipv4().is_none());
+                let actual = actual.ipv6().unwrap().clone();
+                assert_eq!(actual.header.to_header(), ipv6_header);
+                assert_eq!(
+                    Ipv6Extensions::from_slice(
+                        ipv6_header.next_header,
+                        actual.extensions().slice()
+                    ).unwrap().0,
+                    ipv6_exts
+                );
+                assert_eq!(
+                    actual.payload,
+                    IpPayloadSlice{
+                        ip_number: ip_number::UDP.into(),
+                        fragmented: ipv6_exts.is_fragmenting_payload(),
+                        len_source: LenSource::Ipv6HeaderPayloadLen,
+                        payload: &payload
+                    }
+                );
+            }
+
+            // ipv6 with zero payload length (should fallback to the slice length)
+            {
+                let ipv6_header = {
+                    let mut header = ipv6_header.clone();
+                    // set the payload length to zero so the payload identifier
+                    // has to fallback to the slice length
+                    header.payload_length = 0;
+                    header
+                };
+
+                // build packet
+                let mut data = Vec::with_capacity(ipv6.header_len() + payload.len());
+                ipv6_header.write(&mut data).unwrap();
+                ipv6_exts.write(&mut data, ipv6_header.next_header).unwrap();
+                data.extend_from_slice(&payload);
+
+                // run test
+                let actual = crate::IpSlice::from_slice(&data).unwrap();
+                assert!(actual.ipv4().is_none());
+                let actual = actual.ipv6().unwrap().clone();
+                assert_eq!(actual.header.to_header(), ipv6_header);
+                assert_eq!(
+                    Ipv6Extensions::from_slice(
+                        ipv6_header.next_header,
+                        actual.extensions().slice()
+                    ).unwrap().0,
+                    ipv6_exts
+                );
+                assert_eq!(
+                    actual.payload,
+                    IpPayloadSlice{
+                        ip_number: ip_number::UDP.into(),
+                        fragmented: ipv6_exts.is_fragmenting_payload(),
+                        len_source: LenSource::Slice,
+                        payload: &payload
+                    }
+                );
+            }
+
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_ipv4_slice(
+            ipv4_header in ipv4_unknown()
+        ) {
+            let mut header = ipv4_header.clone();
+            header.total_len = (header.header_len() + 4) as u16;
+
+            let mut buffer = Vec::with_capacity(header.total_len.into());
+            buffer.extend_from_slice(&header.to_bytes()[..]);
+            buffer.extend_from_slice(&[1,2,3,4]);
+            let s = Ipv4Slice::from_slice(&buffer).unwrap();
+            let actual: IpSlice = s.clone().into();
+            assert_eq!(IpSlice::Ipv4(s), actual);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_ipv6_slice(
+            ipv6_header in ipv6_unknown()
+        ) {
+            let mut header = ipv6_header.clone();
+            header.payload_length = 4;
+
+            let mut buffer = Vec::with_capacity(header.header_len() + 4);
+            buffer.extend_from_slice(&header.to_bytes()[..]);
+            buffer.extend_from_slice(&[1,2,3,4]);
+            let s = Ipv6Slice::from_slice(&buffer).unwrap();
+            let actual: IpSlice = s.clone().into();
+            assert_eq!(IpSlice::Ipv6(s), actual);
+        }
+    }
+}
diff --git a/src/net/ipv4_dscp.rs b/src/net/ipv4_dscp.rs
new file mode 100644
index 0000000..9498b34
--- /dev/null
+++ b/src/net/ipv4_dscp.rs
@@ -0,0 +1,252 @@
+use crate::err::ValueTooBigError;
+
+/// 6 bit unsigned integer containing the "Differentiated Services
+/// Code Point" (present in the [`crate::Ipv4Header`]).
+#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct Ipv4Dscp(u8);
+
+impl Ipv4Dscp {
+    /// Ipv4Dscp with value 0.
+    pub const ZERO: Ipv4Dscp = Ipv4Dscp(0);
+
+    /// Maximum value of an IPv4 header DSCP.
+    pub const MAX_U8: u8 = 0b0011_1111;
+
+    /// Tries to create an [`Ipv4Dscp`] and checks that the passed value
+    /// is smaller or equal than [`Ipv4Dscp::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 [`Ipv4Dscp`].
+    ///
+    /// ```
+    /// use etherparse::Ipv4Dscp;
+    ///
+    /// let dscp = Ipv4Dscp::try_new(32).unwrap();
+    /// assert_eq!(dscp.value(), 32);
+    ///
+    /// // if a number that can not be represented in an 6 bit integer
+    /// // gets passed in an error is returned
+    /// use etherparse::err::{ValueTooBigError, ValueType};
+    /// assert_eq!(
+    ///     Ipv4Dscp::try_new(Ipv4Dscp::MAX_U8 + 1),
+    ///     Err(ValueTooBigError{
+    ///         actual: Ipv4Dscp::MAX_U8 + 1,
+    ///         max_allowed: Ipv4Dscp::MAX_U8,
+    ///         value_type: ValueType::Ipv4Dscp,
+    ///     })
+    /// );
+    /// ```
+    #[inline]
+    pub const fn try_new(value: u8) -> Result<Ipv4Dscp, ValueTooBigError<u8>> {
+        use crate::err::ValueType;
+        if value <= Ipv4Dscp::MAX_U8 {
+            Ok(Ipv4Dscp(value))
+        } else {
+            Err(ValueTooBigError {
+                actual: value,
+                max_allowed: Ipv4Dscp::MAX_U8,
+                value_type: ValueType::Ipv4Dscp,
+            })
+        }
+    }
+
+    /// Creates an [`Ipv4Dscp`] without checking that the value
+    /// is smaller or equal than [`Ipv4Dscp::MAX_U8`] (6 bit unsigned integer).
+    /// The caller must guarantee that `value <= Ipv4Dscp::MAX_U8`.
+    ///
+    /// # Safety
+    ///
+    /// `value` must be smaller or equal than [`Ipv4Dscp::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) -> Ipv4Dscp {
+        debug_assert!(value <= Ipv4Dscp::MAX_U8);
+        Ipv4Dscp(value)
+    }
+
+    /// 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 Ipv4Dscp {
+    #[inline]
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+impl From<Ipv4Dscp> for u8 {
+    #[inline]
+    fn from(value: Ipv4Dscp) -> Self {
+        value.0
+    }
+}
+
+impl TryFrom<u8> for Ipv4Dscp {
+    type Error = ValueTooBigError<u8>;
+
+    #[inline]
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        use crate::err::ValueType;
+        if value <= Ipv4Dscp::MAX_U8 {
+            Ok(Ipv4Dscp(value))
+        } else {
+            Err(Self::Error {
+                actual: value,
+                max_allowed: Ipv4Dscp::MAX_U8,
+                value_type: ValueType::Ipv4Dscp,
+            })
+        }
+    }
+}
+
+#[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 = Ipv4Dscp(32);
+            let b = a;
+            assert_eq!(a, b);
+            assert_eq!(a.clone(), a);
+        }
+
+        // default
+        {
+            let actual: Ipv4Dscp = Default::default();
+            assert_eq!(actual.value(), 0);
+        }
+
+        // debug
+        {
+            let a = Ipv4Dscp(32);
+            assert_eq!(format!("{:?}", a), format!("Ipv4Dscp(32)"));
+        }
+
+        // ord & partial ord
+        {
+            use core::cmp::Ordering;
+            let a = Ipv4Dscp(32);
+            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();
+                Ipv4Dscp(64).hash(&mut hasher);
+                hasher.finish()
+            };
+            let b = {
+                let mut hasher = DefaultHasher::new();
+                Ipv4Dscp(64).hash(&mut hasher);
+                hasher.finish()
+            };
+            assert_eq!(a, b);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn try_new(
+            valid_value in 0..=0b0011_1111u8,
+            invalid_value in 0b0100_0000u8..=u8::MAX
+        ) {
+            use crate::err::{ValueType, ValueTooBigError};
+            assert_eq!(
+                valid_value,
+                Ipv4Dscp::try_new(valid_value).unwrap().value()
+            );
+            assert_eq!(
+                Ipv4Dscp::try_new(invalid_value).unwrap_err(),
+                ValueTooBigError{
+                    actual: invalid_value,
+                    max_allowed: 0b0011_1111,
+                    value_type:  ValueType::Ipv4Dscp
+                }
+            );
+        }
+    }
+
+    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: Ipv4Dscp = valid_value.try_into().unwrap();
+                assert_eq!(actual.value(), valid_value);
+
+                let err: Result<Ipv4Dscp, ValueTooBigError<u8>> = invalid_value.try_into();
+                assert_eq!(
+                    err.unwrap_err(),
+                    ValueTooBigError{
+                        actual: invalid_value,
+                        max_allowed: 0b0011_1111,
+                        value_type:  ValueType::Ipv4Dscp
+                    }
+                );
+            }
+            // try_from
+            {
+                assert_eq!(
+                    Ipv4Dscp::try_from(valid_value).unwrap().value(),
+                    valid_value
+                );
+
+                assert_eq!(
+                    Ipv4Dscp::try_from(invalid_value).unwrap_err(),
+                    ValueTooBigError{
+                        actual: invalid_value,
+                        max_allowed: 0b0011_1111,
+                        value_type:  ValueType::Ipv4Dscp
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn new_unchecked(valid_value in 0..=0b0011_1111u8) {
+            assert_eq!(
+                valid_value,
+                unsafe {
+                    Ipv4Dscp::new_unchecked(valid_value).value()
+                }
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn fmt(valid_value in 0..=0b0011_1111u8) {
+            assert_eq!(format!("{}", Ipv4Dscp(valid_value)), format!("{}", valid_value));
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from(valid_value in 0..=0b0011_1111u8,) {
+            let dscp = Ipv4Dscp::try_new(valid_value).unwrap();
+            let actual: u8 = dscp.into();
+            assert_eq!(actual, valid_value);
+        }
+    }
+}
diff --git a/src/net/ipv4_ecn.rs b/src/net/ipv4_ecn.rs
new file mode 100644
index 0000000..d9a5e5d
--- /dev/null
+++ b/src/net/ipv4_ecn.rs
@@ -0,0 +1,261 @@
+use crate::err::ValueTooBigError;
+
+/// 2 bit unsigned integer containing the "Explicit Congestion
+/// Notification" (present in the [`crate::Ipv4Header`]).
+#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct Ipv4Ecn(u8);
+
+impl Ipv4Ecn {
+    /// Ipv4Ecn with value 0.
+    pub const ZERO: Ipv4Ecn = Ipv4Ecn(0);
+
+    /// Ipv4Ecn with value 0.
+    pub const ONE: Ipv4Ecn = Ipv4Ecn(1);
+
+    /// Ipv4Ecn with value 0.
+    pub const TWO: Ipv4Ecn = Ipv4Ecn(2);
+
+    /// Ipv4Ecn with value 0.
+    pub const TRHEE: Ipv4Ecn = Ipv4Ecn(3);
+
+    /// Maximum value of an IPv4 header ECN.
+    pub const MAX_U8: u8 = 0b0000_0011;
+
+    /// Tries to create an [`Ipv4Ecn`] and checks that the passed value
+    /// is smaller or equal than [`Ipv4Ecn::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 [`Ipv4Ecn`].
+    ///
+    /// ```
+    /// use etherparse::Ipv4Ecn;
+    ///
+    /// let ecn = Ipv4Ecn::try_new(2).unwrap();
+    /// assert_eq!(ecn.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!(
+    ///     Ipv4Ecn::try_new(Ipv4Ecn::MAX_U8 + 1),
+    ///     Err(ValueTooBigError{
+    ///         actual: Ipv4Ecn::MAX_U8 + 1,
+    ///         max_allowed: Ipv4Ecn::MAX_U8,
+    ///         value_type: ValueType::Ipv4Ecn,
+    ///     })
+    /// );
+    /// ```
+    #[inline]
+    pub const fn try_new(value: u8) -> Result<Ipv4Ecn, ValueTooBigError<u8>> {
+        use crate::err::ValueType;
+        if value <= Ipv4Ecn::MAX_U8 {
+            Ok(Ipv4Ecn(value))
+        } else {
+            Err(ValueTooBigError {
+                actual: value,
+                max_allowed: Ipv4Ecn::MAX_U8,
+                value_type: ValueType::Ipv4Ecn,
+            })
+        }
+    }
+
+    /// Creates an [`Ipv4Ecn`] without checking that the value
+    /// is smaller or equal than [`Ipv4Ecn::MAX_U8`] (2 bit unsigned integer).
+    /// The caller must guarantee that `value <= Ipv4Ecn::MAX_U8`.
+    ///
+    /// # Safety
+    ///
+    /// `value` must be smaller or equal than [`Ipv4Ecn::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) -> Ipv4Ecn {
+        debug_assert!(value <= Ipv4Ecn::MAX_U8);
+        Ipv4Ecn(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 Ipv4Ecn {
+    #[inline]
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+impl From<Ipv4Ecn> for u8 {
+    #[inline]
+    fn from(value: Ipv4Ecn) -> Self {
+        value.0
+    }
+}
+
+impl TryFrom<u8> for Ipv4Ecn {
+    type Error = ValueTooBigError<u8>;
+
+    #[inline]
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        use crate::err::ValueType;
+        if value <= Ipv4Ecn::MAX_U8 {
+            Ok(Ipv4Ecn(value))
+        } else {
+            Err(Self::Error {
+                actual: value,
+                max_allowed: Ipv4Ecn::MAX_U8,
+                value_type: ValueType::Ipv4Ecn,
+            })
+        }
+    }
+}
+
+#[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 = Ipv4Ecn(2);
+            let b = a;
+            assert_eq!(a, b);
+            assert_eq!(a.clone(), a);
+        }
+
+        // default
+        {
+            let actual: Ipv4Ecn = Default::default();
+            assert_eq!(actual.value(), 0);
+        }
+
+        // debug
+        {
+            let a = Ipv4Ecn(2);
+            assert_eq!(format!("{:?}", a), format!("Ipv4Ecn(2)"));
+        }
+
+        // ord & partial ord
+        {
+            use core::cmp::Ordering;
+            let a = Ipv4Ecn(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();
+                Ipv4Ecn(2).hash(&mut hasher);
+                hasher.finish()
+            };
+            let b = {
+                let mut hasher = DefaultHasher::new();
+                Ipv4Ecn(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,
+                Ipv4Ecn::try_new(valid_value).unwrap().value()
+            );
+            assert_eq!(
+                Ipv4Ecn::try_new(invalid_value).unwrap_err(),
+                ValueTooBigError{
+                    actual: invalid_value,
+                    max_allowed: 0b0000_0011,
+                    value_type:  ValueType::Ipv4Ecn
+                }
+            );
+        }
+    }
+
+    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: Ipv4Ecn = valid_value.try_into().unwrap();
+                assert_eq!(actual.value(), valid_value);
+
+                let err: Result<Ipv4Ecn, ValueTooBigError<u8>> = invalid_value.try_into();
+                assert_eq!(
+                    err.unwrap_err(),
+                    ValueTooBigError{
+                        actual: invalid_value,
+                        max_allowed: 0b0000_0011,
+                        value_type:  ValueType::Ipv4Ecn
+                    }
+                );
+            }
+            // try_from
+            {
+                assert_eq!(
+                    Ipv4Ecn::try_from(valid_value).unwrap().value(),
+                    valid_value
+                );
+
+                assert_eq!(
+                    Ipv4Ecn::try_from(invalid_value).unwrap_err(),
+                    ValueTooBigError{
+                        actual: invalid_value,
+                        max_allowed: 0b0000_0011,
+                        value_type:  ValueType::Ipv4Ecn
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn new_unchecked(valid_value in 0..=0b0000_0011u8) {
+            assert_eq!(
+                valid_value,
+                unsafe {
+                    Ipv4Ecn::new_unchecked(valid_value).value()
+                }
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn fmt(valid_value in 0..=0b0000_0011u8) {
+            assert_eq!(format!("{}", Ipv4Ecn(valid_value)), format!("{}", valid_value));
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from(valid_value in 0..=0b0000_0011u8,) {
+            let ecn = Ipv4Ecn::try_new(valid_value).unwrap();
+            let actual: u8 = ecn.into();
+            assert_eq!(actual, valid_value);
+        }
+    }
+}
diff --git a/src/net/ipv4_exts.rs b/src/net/ipv4_exts.rs
new file mode 100644
index 0000000..1c9ff7e
--- /dev/null
+++ b/src/net/ipv4_exts.rs
@@ -0,0 +1,597 @@
+use crate::{err::ipv4_exts::ExtsWalkError, *};
+
+/// IPv4 extension headers present after the ip header.
+///
+/// Currently supported:
+/// * Authentication Header
+///
+/// Currently not supported:
+/// - Encapsulating Security Payload Header (ESP)
+#[derive(Clone, Debug, Eq, PartialEq, Default)]
+pub struct Ipv4Extensions {
+    pub auth: Option<IpAuthHeader>,
+}
+
+impl Ipv4Extensions {
+    /// Minimum length required for extension header in bytes/octets.
+    /// Which is zero as no extension headers are required.
+    pub const MIN_LEN: usize = 0;
+
+    /// Maximum summed up length of all extension headers in bytes/octets.
+    pub const MAX_LEN: usize = IpAuthHeader::MAX_LEN;
+
+    /// Read all known ipv4 extensions and return an `Ipv4Extensions` with the
+    /// identified slices, the final ip number and a slice pointing to the non parsed data.
+    pub fn from_slice(
+        start_ip_number: IpNumber,
+        slice: &[u8],
+    ) -> Result<(Ipv4Extensions, IpNumber, &[u8]), err::ip_auth::HeaderSliceError> {
+        Ipv4ExtensionsSlice::from_slice(start_ip_number, slice).map(|v| (v.0.to_header(), v.1, v.2))
+    }
+
+    /// Collects all known ipv4 extension headers in a slice until an error
+    /// is encountered or a "non IP extension header" is found and
+    /// returns the successfully parsed parts (+ the unparsed slice
+    /// it's [`IpNumber`] and the error if one occurred).
+    ///
+    /// The returned values are
+    ///
+    /// * [`Ipv4Extensions`] containing the successfully parsed IPv6 extension headers
+    /// * [`IpNumber`] of unparsed data
+    /// * Slice with unparsed data
+    /// * Optional with error if there was an error wich stoped the parsing.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use etherparse::{Ipv4Extensions, IpAuthHeader, ip_number::{UDP, AUTHENTICATION_HEADER}};
+    ///
+    /// let auth_header = IpAuthHeader::new(UDP, 0, 0, &[]).unwrap();
+    /// let data = auth_header.to_bytes();
+    ///
+    /// let (ipv4_exts, next_ip_num, next_data, err) =
+    ///     Ipv4Extensions::from_slice_lax(AUTHENTICATION_HEADER, &data);
+    ///
+    /// // authentication header is separated and no error occurred
+    /// assert!(ipv4_exts.auth.is_some());
+    /// assert_eq!(next_ip_num, UDP);
+    /// assert_eq!(next_data, &[]);
+    /// assert!(err.is_none());
+    /// ```
+    ///
+    /// It is also ok to pass in a "non ip extension":
+    ///
+    /// ```
+    /// use etherparse::{Ipv4Extensions, ip_number::UDP};
+    ///
+    /// let data = [0,1,2,3];
+    /// // passing a non "ip extension header" ip number
+    /// let (ipv4_exts, next_ip_num, next_data, err) =
+    ///     Ipv4Extensions::from_slice_lax(UDP, &data);
+    ///
+    /// // the original data gets returned as UDP is not a
+    /// // an IP extension header
+    /// assert!(ipv4_exts.is_empty());
+    /// assert_eq!(next_ip_num, UDP);
+    /// assert_eq!(next_data, &data);
+    /// // no errors gets triggered as the data is valid
+    /// assert!(err.is_none());
+    /// ```
+    ///
+    /// In case an error occurred the original data gets
+    /// returned together with the error:
+    ///
+    /// ```
+    /// use etherparse::{
+    ///     Ipv4Extensions,
+    ///     IpAuthHeader,
+    ///     ip_number::AUTHENTICATION_HEADER,
+    ///     LenSource,
+    ///     err::{ip_auth::HeaderSliceError::Len, LenError, Layer}
+    /// };
+    ///
+    /// // providing not enough data
+    /// let (ipv4_exts, next_ip_num, next_data, err) =
+    ///     Ipv4Extensions::from_slice_lax(AUTHENTICATION_HEADER, &[]);
+    ///
+    /// // original data will be returned with no data parsed
+    /// assert!(ipv4_exts.is_empty());
+    /// assert_eq!(next_ip_num, AUTHENTICATION_HEADER);
+    /// assert_eq!(next_data, &[]);
+    /// // the error that stopped the parsing will also be returned
+    /// assert_eq!(err, Some(Len(LenError{
+    ///     required_len: IpAuthHeader::MIN_LEN,
+    ///     len: 0,
+    ///     len_source: LenSource::Slice,
+    ///     layer: Layer::IpAuthHeader,
+    ///     layer_start_offset: 0,
+    /// })));
+    /// ```
+    pub fn from_slice_lax(
+        start_ip_number: IpNumber,
+        start_slice: &[u8],
+    ) -> (
+        Ipv4Extensions,
+        IpNumber,
+        &[u8],
+        Option<err::ip_auth::HeaderSliceError>,
+    ) {
+        let (slice, next_ip_number, next_data, error) =
+            Ipv4ExtensionsSlice::from_slice_lax(start_ip_number, start_slice);
+        (slice.to_header(), next_ip_number, next_data, error)
+    }
+
+    /// Reads the known ipv4 extension headers from the reader and returns the
+    /// headers together with the internet protocol number identifying the protocol
+    /// that will be next.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read<T: std::io::Read + Sized>(
+        reader: &mut T,
+        start_ip_number: IpNumber,
+    ) -> Result<(Ipv4Extensions, IpNumber), err::ip_auth::HeaderReadError> {
+        use ip_number::*;
+        if AUTH == start_ip_number {
+            let header = IpAuthHeader::read(reader)?;
+            let next_ip_number = header.next_header;
+            Ok((Ipv4Extensions { auth: Some(header) }, next_ip_number))
+        } else {
+            Ok((Default::default(), start_ip_number))
+        }
+    }
+
+    /// Reads the known ipv4 extension headers from a length limited reader and returns the
+    /// headers together with the internet protocol number identifying the protocol
+    /// that will be next.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read_limited<T: std::io::Read + Sized>(
+        reader: &mut crate::io::LimitedReader<T>,
+        start_ip_number: IpNumber,
+    ) -> Result<(Ipv4Extensions, IpNumber), err::ip_auth::HeaderLimitedReadError> {
+        use ip_number::*;
+        if AUTH == start_ip_number {
+            let header = IpAuthHeader::read_limited(reader)?;
+            let next_ip_number = header.next_header;
+            Ok((Ipv4Extensions { auth: Some(header) }, next_ip_number))
+        } else {
+            Ok((Default::default(), start_ip_number))
+        }
+    }
+
+    /// Write the extensions to the writer.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn write<T: std::io::Write + Sized>(
+        &self,
+        writer: &mut T,
+        start_ip_number: IpNumber,
+    ) -> Result<(), err::ipv4_exts::HeaderWriteError> {
+        use err::ipv4_exts::{ExtsWalkError::*, HeaderWriteError::*};
+        use ip_number::*;
+        match self.auth {
+            Some(ref header) => {
+                if AUTH == start_ip_number {
+                    header.write(writer).map_err(Io)
+                } else {
+                    Err(Content(ExtNotReferenced {
+                        missing_ext: IpNumber::AUTHENTICATION_HEADER,
+                    }))
+                }
+            }
+            None => Ok(()),
+        }
+    }
+
+    ///Length of the all present headers in bytes.
+    pub fn header_len(&self) -> usize {
+        if let Some(ref header) = self.auth {
+            header.header_len()
+        } else {
+            0
+        }
+    }
+
+    /// Sets all the next_header fields of the headers based on the adviced default order
+    /// with the given protocol number as last "next header" value. The return value is the protocol
+    /// number of the first existing extension header that should be entered in the ipv4 header as
+    /// protocol_number.
+    ///
+    /// If no extension headers are present the value of the argument is returned.
+    pub fn set_next_headers(&mut self, last_protocol_number: IpNumber) -> IpNumber {
+        use ip_number::*;
+
+        let mut next = last_protocol_number;
+
+        if let Some(ref mut header) = self.auth {
+            header.next_header = next;
+            next = AUTH;
+        }
+
+        next
+    }
+
+    /// Return next header based on the extension headers and
+    /// the first ip protocol number.
+    ///
+    /// In case a header is never referenced a
+    /// [`err::ipv4_exts::ExtsWalkError::ExtNotReferenced`] is returned.
+    pub fn next_header(&self, first_next_header: IpNumber) -> Result<IpNumber, ExtsWalkError> {
+        use ip_number::*;
+        if let Some(ref auth) = self.auth {
+            if first_next_header == AUTH {
+                Ok(auth.next_header)
+            } else {
+                Err(ExtsWalkError::ExtNotReferenced {
+                    missing_ext: IpNumber::AUTHENTICATION_HEADER,
+                })
+            }
+        } else {
+            Ok(first_next_header)
+        }
+    }
+
+    /// Returns true if no IPv4 extension header is present (all fields `None`).
+    #[inline]
+    pub fn is_empty(&self) -> bool {
+        self.auth.is_none()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::ip_number::*;
+    use crate::test_gens::*;
+    use alloc::vec::Vec;
+    use proptest::prelude::*;
+    use std::io::Cursor;
+
+    #[test]
+    fn from_slice() {
+        let auth_header = IpAuthHeader::new(UDP, 0, 0, &[]).unwrap();
+
+        let buffer = {
+            let mut buffer = Vec::with_capacity(auth_header.header_len());
+            auth_header.write(&mut buffer).unwrap();
+            buffer.push(1);
+            buffer.push(2);
+            buffer
+        };
+
+        // no auth header
+        {
+            let (header, next, rest) = Ipv4Extensions::from_slice(TCP, &buffer).unwrap();
+            assert!(header.auth.is_none());
+            assert_eq!(TCP, next);
+            assert_eq!(rest, &buffer);
+        }
+
+        // with auth header
+        {
+            let (actual, next, rest) = Ipv4Extensions::from_slice(AUTH, &buffer).unwrap();
+            assert_eq!(actual.auth.unwrap(), auth_header);
+            assert_eq!(UDP, next);
+            assert_eq!(rest, &buffer[auth_header.header_len()..]);
+        }
+
+        // too small
+        {
+            use err::ip_auth::HeaderSliceError::Len;
+            const AUTH_HEADER_LEN: usize = 12;
+            assert_eq!(
+                Ipv4Extensions::from_slice(AUTH, &buffer[..auth_header.header_len() - 1])
+                    .unwrap_err(),
+                Len(err::LenError {
+                    required_len: AUTH_HEADER_LEN,
+                    len: auth_header.header_len() - 1,
+                    len_source: LenSource::Slice,
+                    layer: err::Layer::IpAuthHeader,
+                    layer_start_offset: 0,
+                })
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice_lax(auth in ip_auth_any()) {
+            use crate::ip_number::{UDP, AUTHENTICATION_HEADER};
+            use crate::err::{*, ip_auth::HeaderSliceError::Len};
+
+            // normal read
+            {
+                let data = auth.to_bytes();
+
+                let (ipv4_exts, next_ip_num, next_data, err) =
+                    Ipv4Extensions::from_slice_lax(AUTHENTICATION_HEADER, &data);
+
+                // authentication header is separated and no error occurred
+                assert_eq!(ipv4_exts.auth, Some(auth.clone()));
+                assert_eq!(next_ip_num, auth.next_header);
+                assert_eq!(next_data, &[]);
+                assert!(err.is_none());
+            }
+            // normal read with no extension header
+            {
+                let data = [0,1,2,3];
+                // passing a non "ip extension header" ip number
+                let (ipv4_exts, next_ip_num, next_data, err) =
+                    Ipv4Extensions::from_slice_lax(UDP, &data);
+
+                // the original data gets returned as UDP is not a
+                // an IP extension header
+                assert!(ipv4_exts.is_empty());
+                assert_eq!(next_ip_num, UDP);
+                assert_eq!(next_data, &data);
+                // no errors gets triggered as the data is valid
+                assert!(err.is_none());
+            }
+            // len error during parsing
+            {
+                // providing not enough data
+                let (ipv4_exts, next_ip_num, next_data, err) =
+                    Ipv4Extensions::from_slice_lax(AUTHENTICATION_HEADER, &[]);
+
+                // original data will be returned with no data parsed
+                assert!(ipv4_exts.is_empty());
+                assert_eq!(next_ip_num, AUTHENTICATION_HEADER);
+                assert_eq!(next_data, &[]);
+                // the error that stopped the parsing will also be returned
+                assert_eq!(err, Some(Len(LenError{
+                    required_len: IpAuthHeader::MIN_LEN,
+                    len: 0,
+                    len_source: LenSource::Slice,
+                    layer: Layer::IpAuthHeader,
+                    layer_start_offset: 0,
+                })));
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read(auth in ip_auth_any()) {
+            // None
+            {
+                let mut cursor = Cursor::new(&[]);
+                let (actual, next) = Ipv4Extensions::read(&mut cursor, UDP).unwrap();
+                assert_eq!(next, UDP);
+                assert_eq!(
+                    actual,
+                    Ipv4Extensions{
+                        auth: None,
+                    }
+                );
+            }
+
+            // Some sucessfull
+            {
+                let buffer = {
+                    let mut buffer = Vec::with_capacity(auth.header_len());
+                    auth.write(&mut buffer).unwrap();
+                    buffer.push(1);
+                    buffer
+                };
+                let mut cursor = Cursor::new(&buffer);
+                let (actual, next) = Ipv4Extensions::read(&mut cursor, AUTH).unwrap();
+                assert_eq!(auth.header_len(), cursor.position() as usize);
+                assert_eq!(next, auth.next_header);
+                assert_eq!(
+                    actual,
+                    Ipv4Extensions{
+                        auth: Some(auth.clone()),
+                    }
+                );
+            }
+
+            // Some error
+            {
+                let mut cursor = Cursor::new(&[]);
+                assert!(Ipv4Extensions::read(&mut cursor, AUTH).is_err());
+            }
+        }
+    }
+
+    #[test]
+    fn write() {
+        // None
+        {
+            let mut buffer = Vec::new();
+            Ipv4Extensions { auth: None }
+                .write(&mut buffer, UDP)
+                .unwrap();
+            assert_eq!(0, buffer.len());
+        }
+
+        // Some
+        let auth_header = IpAuthHeader::new(UDP, 0, 0, &[]).unwrap();
+        {
+            let mut buffer = Vec::with_capacity(auth_header.header_len());
+            Ipv4Extensions {
+                auth: Some(auth_header.clone()),
+            }
+            .write(&mut buffer, AUTH)
+            .unwrap();
+            let (read_header, _) = IpAuthHeader::from_slice(&buffer).unwrap();
+            assert_eq!(auth_header, read_header);
+        }
+
+        // Some bad start number
+        {
+            use crate::err::ipv4_exts::ExtsWalkError::ExtNotReferenced;
+
+            let mut buffer = Vec::new();
+            let err = Ipv4Extensions {
+                auth: Some(auth_header.clone()),
+            }
+            .write(&mut buffer, UDP)
+            .unwrap_err();
+            assert_eq!(
+                err.content().unwrap(),
+                &ExtNotReferenced {
+                    missing_ext: IpNumber::AUTHENTICATION_HEADER,
+                }
+            );
+        }
+
+        // Some: Write error
+        {
+            let mut buffer = Vec::with_capacity(auth_header.header_len() - 1);
+            buffer.resize(auth_header.header_len() - 1, 0);
+            let mut cursor = Cursor::new(&mut buffer[..]);
+            let err = Ipv4Extensions {
+                auth: Some(auth_header.clone()),
+            }
+            .write(&mut cursor, AUTH)
+            .unwrap_err();
+            assert!(err.io().is_some());
+        }
+    }
+
+    #[test]
+    fn header_len() {
+        // None
+        assert_eq!(0, Ipv4Extensions { auth: None }.header_len());
+
+        // Some
+        {
+            let auth = IpAuthHeader::new(UDP, 0, 0, &[]).unwrap();
+            assert_eq!(
+                auth.header_len(),
+                Ipv4Extensions { auth: Some(auth) }.header_len()
+            );
+        }
+        // Some with paylaod
+        {
+            let auth = IpAuthHeader::new(UDP, 0, 0, &[1, 2, 3, 4]).unwrap();
+            assert_eq!(
+                auth.header_len(),
+                Ipv4Extensions { auth: Some(auth) }.header_len()
+            );
+        }
+    }
+
+    #[test]
+    fn set_next_headers() {
+        // None
+        {
+            let mut exts = Ipv4Extensions { auth: None };
+            assert_eq!(UDP, exts.set_next_headers(UDP));
+        }
+
+        // Some
+        {
+            let mut exts = Ipv4Extensions {
+                auth: Some(IpAuthHeader::new(TCP, 0, 0, &[]).unwrap()),
+            };
+            assert_eq!(TCP, exts.auth.as_ref().unwrap().next_header);
+            // change from TCP to UDP
+            let re = exts.set_next_headers(UDP);
+            assert_eq!(AUTH, re);
+            assert_eq!(UDP, exts.auth.as_ref().unwrap().next_header);
+        }
+    }
+
+    #[test]
+    fn next_header() {
+        // None
+        {
+            let exts = Ipv4Extensions { auth: None };
+            assert_eq!(UDP, exts.next_header(UDP).unwrap());
+        }
+        // Some
+        {
+            let exts = Ipv4Extensions {
+                auth: Some(IpAuthHeader::new(TCP, 0, 0, &[]).unwrap()),
+            };
+
+            // auth referenced
+            assert_eq!(TCP, exts.next_header(AUTH).unwrap());
+
+            // auth not referenced (error)
+            use crate::err::ipv4_exts::ExtsWalkError::ExtNotReferenced;
+            assert_eq!(
+                ExtNotReferenced {
+                    missing_ext: IpNumber::AUTHENTICATION_HEADER
+                },
+                exts.next_header(TCP).unwrap_err()
+            );
+        }
+    }
+
+    #[test]
+    fn is_empty() {
+        // empty
+        assert!(Ipv4Extensions { auth: None }.is_empty());
+
+        // auth
+        assert_eq!(
+            false,
+            Ipv4Extensions {
+                auth: Some(IpAuthHeader::new(ip_number::UDP, 0, 0, &[]).unwrap()),
+            }
+            .is_empty()
+        );
+    }
+
+    proptest! {
+        #[test]
+        fn debug(auth in ip_auth_any()) {
+            use alloc::format;
+
+            // None
+            assert_eq!(
+                &format!("Ipv4Extensions {{ auth: {:?} }}", Option::<IpAuthHeader>::None),
+                &format!(
+                    "{:?}",
+                    Ipv4Extensions {
+                        auth: None,
+                    }
+                )
+            );
+
+            // Some
+            assert_eq!(
+                &format!("Ipv4Extensions {{ auth: {:?} }}", Some(auth.clone())),
+                &format!(
+                    "{:?}",
+                    Ipv4Extensions {
+                        auth: Some(auth.clone()),
+                    }
+                )
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(auth in ip_auth_any()) {
+            // None
+            {
+                let header = Ipv4Extensions{
+                    auth: None,
+                };
+                assert_eq!(
+                    header.clone(),
+                    Ipv4Extensions{
+                        auth: None,
+                    }
+                );
+            }
+
+            // Some
+            {
+                let header = Ipv4Extensions{
+                    auth: Some(auth.clone()),
+                };
+                assert_eq!(
+                    header.clone(),
+                    Ipv4Extensions{
+                        auth: Some(auth.clone()),
+                    }
+                );
+            }
+        }
+    }
+}
diff --git a/src/net/ipv4_exts_slice.rs b/src/net/ipv4_exts_slice.rs
new file mode 100644
index 0000000..f29fa37
--- /dev/null
+++ b/src/net/ipv4_exts_slice.rs
@@ -0,0 +1,367 @@
+use crate::*;
+
+/// Slices of the IPv4 extension headers present after the ip header.
+///
+/// Currently supported:
+/// * Authentication Header
+///
+/// Currently not supported:
+/// * Encapsulating Security Payload Header (ESP)
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
+pub struct Ipv4ExtensionsSlice<'a> {
+    pub auth: Option<IpAuthHeaderSlice<'a>>,
+}
+
+impl<'a> Ipv4ExtensionsSlice<'a> {
+    /// Read all known ipv4 extensions and return an `Ipv4ExtensionSlices` with the
+    /// identified slices, the final ip number and a slice pointing to the non parsed data.
+    pub fn from_slice(
+        start_ip_number: IpNumber,
+        start_slice: &'a [u8],
+    ) -> Result<(Ipv4ExtensionsSlice<'a>, IpNumber, &'a [u8]), err::ip_auth::HeaderSliceError> {
+        use ip_number::*;
+        if AUTH == start_ip_number {
+            let header = IpAuthHeaderSlice::from_slice(start_slice)?;
+            let rest = &start_slice[header.slice().len()..];
+            let next_header = header.next_header();
+            Ok((
+                Ipv4ExtensionsSlice { auth: Some(header) },
+                next_header,
+                rest,
+            ))
+        } else {
+            Ok((Default::default(), start_ip_number, start_slice))
+        }
+    }
+
+    /// Collects all ipv4 extension headers in a slice until an error
+    /// is encountered or a "non IP extension header" is found and
+    /// returns the successfully parsed parts (+ the unparsed slice
+    /// it's [`IpNumber`] and the error if one occurred).
+    ///
+    /// The returned values are
+    ///
+    /// * [`Ipv4ExtensionsSlice`] containing the successfully parsed IPv6 extension headers
+    /// * [`IpNumber`] of unparsed data
+    /// * Slice with unparsed data
+    /// * Optional with error if there was an error wich stoped the parsing.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use etherparse::{Ipv4ExtensionsSlice, IpAuthHeader, ip_number::{UDP, AUTHENTICATION_HEADER}};
+    ///
+    /// let auth_header = IpAuthHeader::new(UDP, 0, 0, &[]).unwrap();
+    /// let data = auth_header.to_bytes();
+    ///
+    /// let (ipv4_exts, next_ip_num, next_data, err) =
+    ///     Ipv4ExtensionsSlice::from_slice_lax(AUTHENTICATION_HEADER, &data);
+    ///
+    /// // authentication header is separated and no error occurred
+    /// assert!(ipv4_exts.auth.is_some());
+    /// assert_eq!(next_ip_num, UDP);
+    /// assert_eq!(next_data, &[]);
+    /// assert!(err.is_none());
+    /// ```
+    ///
+    /// It is also ok to pass in a "non ip extension":
+    ///
+    /// ```
+    /// use etherparse::{Ipv4ExtensionsSlice, ip_number::UDP};
+    ///
+    /// let data = [0,1,2,3];
+    /// // passing a non "ip extension header" ip number
+    /// let (ipv4_exts, next_ip_num, next_data, err) =
+    ///     Ipv4ExtensionsSlice::from_slice_lax(UDP, &data);
+    ///
+    /// // the original data gets returned as UDP is not a
+    /// // an IP extension header
+    /// assert!(ipv4_exts.is_empty());
+    /// assert_eq!(next_ip_num, UDP);
+    /// assert_eq!(next_data, &data);
+    /// // no errors gets triggered as the data is valid
+    /// assert!(err.is_none());
+    /// ```
+    ///
+    /// In case an error occurred the original data gets
+    /// returned together with the error:
+    ///
+    /// ```
+    /// use etherparse::{
+    ///     Ipv4ExtensionsSlice,
+    ///     IpAuthHeader,
+    ///     ip_number::AUTHENTICATION_HEADER,
+    ///     LenSource,
+    ///     err::{ip_auth::HeaderSliceError::Len, LenError, Layer}
+    /// };
+    ///
+    /// // providing not enough data
+    /// let (ipv4_exts, next_ip_num, next_data, err) =
+    ///     Ipv4ExtensionsSlice::from_slice_lax(AUTHENTICATION_HEADER, &[]);
+    ///
+    /// // original data will be returned with no data parsed
+    /// assert!(ipv4_exts.is_empty());
+    /// assert_eq!(next_ip_num, AUTHENTICATION_HEADER);
+    /// assert_eq!(next_data, &[]);
+    /// // the error that stopped the parsing will also be returned
+    /// assert_eq!(err, Some(Len(LenError{
+    ///     required_len: IpAuthHeader::MIN_LEN,
+    ///     len: 0,
+    ///     len_source: LenSource::Slice,
+    ///     layer: Layer::IpAuthHeader,
+    ///     layer_start_offset: 0,
+    /// })));
+    /// ```
+    pub fn from_slice_lax(
+        start_ip_number: IpNumber,
+        start_slice: &'a [u8],
+    ) -> (
+        Ipv4ExtensionsSlice<'a>,
+        IpNumber,
+        &'a [u8],
+        Option<err::ip_auth::HeaderSliceError>,
+    ) {
+        use ip_number::*;
+        if AUTH == start_ip_number {
+            match IpAuthHeaderSlice::from_slice(start_slice) {
+                Ok(header) => {
+                    let rest = unsafe {
+                        // SAFE as header.slice() has the same start and is a
+                        // subslice of start_slice.
+                        core::slice::from_raw_parts(
+                            start_slice.as_ptr().add(header.slice().len()),
+                            start_slice.len() - header.slice().len(),
+                        )
+                    };
+                    let next_header = header.next_header();
+                    (
+                        Ipv4ExtensionsSlice { auth: Some(header) },
+                        next_header,
+                        rest,
+                        None,
+                    )
+                }
+                Err(err) => (
+                    Ipv4ExtensionsSlice { auth: None },
+                    start_ip_number,
+                    start_slice,
+                    Some(err),
+                ),
+            }
+        } else {
+            (
+                Ipv4ExtensionsSlice { auth: None },
+                start_ip_number,
+                start_slice,
+                None,
+            )
+        }
+    }
+
+    /// Convert the slices into actual headers.
+    pub fn to_header(&self) -> Ipv4Extensions {
+        Ipv4Extensions {
+            auth: self.auth.as_ref().map(|v| v.to_header()),
+        }
+    }
+
+    /// Returns true if no IPv4 extension header is present (all fields `None`).
+    #[inline]
+    pub fn is_empty(&self) -> bool {
+        self.auth.is_none()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::vec::Vec;
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn debug(auth in ip_auth_any()) {
+            use alloc::format;
+
+            // None
+            assert_eq!(
+                &format!("Ipv4ExtensionsSlice {{ auth: {:?} }}", Option::<IpAuthHeader>::None),
+                &format!(
+                    "{:?}",
+                    Ipv4ExtensionsSlice {
+                        auth: None,
+                    }
+                )
+            );
+
+            // Some
+            let buffer = {
+                let mut buffer = Vec::with_capacity(auth.header_len());
+                auth.write(&mut buffer).unwrap();
+                buffer
+            };
+            let auth_slice = IpAuthHeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(
+                &format!("Ipv4ExtensionsSlice {{ auth: {:?} }}", Some(auth_slice.clone())),
+                &format!(
+                    "{:?}",
+                    Ipv4ExtensionsSlice {
+                        auth: Some(auth_slice.clone()),
+                    }
+                )
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(auth in ip_auth_any()) {
+            // None
+            {
+                let header = Ipv4ExtensionsSlice{
+                    auth: None,
+                };
+                assert_eq!(
+                    header.clone(),
+                    Ipv4ExtensionsSlice{
+                        auth: None,
+                    }
+                );
+            }
+
+            // Some
+            {
+                let buffer = {
+                    let mut buffer = Vec::with_capacity(auth.header_len());
+                    auth.write(&mut buffer).unwrap();
+                    buffer
+                };
+                let auth_slice = IpAuthHeaderSlice::from_slice(&buffer).unwrap();
+                let slice = Ipv4ExtensionsSlice {
+                    auth: Some(auth_slice.clone()),
+                };
+                assert_eq!(
+                    slice.clone(),
+                    Ipv4ExtensionsSlice{
+                        auth: Some(auth_slice.clone()),
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice_lax(auth in ip_auth_any()) {
+            use crate::ip_number::{UDP, AUTHENTICATION_HEADER};
+            use crate::err::{*, ip_auth::HeaderSliceError::Len};
+
+            // normal read
+            {
+                let data = auth.to_bytes();
+
+                let (ipv4_exts, next_ip_num, next_data, err) =
+                    Ipv4ExtensionsSlice::from_slice_lax(AUTHENTICATION_HEADER, &data);
+
+                // authentication header is separated and no error occurred
+                assert_eq!(ipv4_exts.auth.unwrap().to_header(), auth);
+                assert_eq!(next_ip_num, auth.next_header);
+                assert_eq!(next_data, &[]);
+                assert!(err.is_none());
+            }
+            // normal read with no extension header
+            {
+                let data = [0,1,2,3];
+                // passing a non "ip extension header" ip number
+                let (ipv4_exts, next_ip_num, next_data, err) =
+                    Ipv4ExtensionsSlice::from_slice_lax(UDP, &data);
+
+                // the original data gets returned as UDP is not a
+                // an IP extension header
+                assert!(ipv4_exts.is_empty());
+                assert_eq!(next_ip_num, UDP);
+                assert_eq!(next_data, &data);
+                // no errors gets triggered as the data is valid
+                assert!(err.is_none());
+            }
+            // len error during parsing
+            {
+                // providing not enough data
+                let (ipv4_exts, next_ip_num, next_data, err) =
+                    Ipv4ExtensionsSlice::from_slice_lax(AUTHENTICATION_HEADER, &[]);
+
+                // original data will be returned with no data parsed
+                assert!(ipv4_exts.is_empty());
+                assert_eq!(next_ip_num, AUTHENTICATION_HEADER);
+                assert_eq!(next_data, &[]);
+                // the error that stopped the parsing will also be returned
+                assert_eq!(err, Some(Len(LenError{
+                    required_len: IpAuthHeader::MIN_LEN,
+                    len: 0,
+                    len_source: LenSource::Slice,
+                    layer: Layer::IpAuthHeader,
+                    layer_start_offset: 0,
+                })));
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_header(auth in ip_auth_any()) {
+            // None
+            assert_eq!(
+                Ipv4ExtensionsSlice{
+                    auth: None,
+                }.to_header(),
+                Ipv4Extensions{
+                    auth: None,
+                }
+            );
+
+            // Some
+            {
+                let buffer = {
+                    let mut buffer = Vec::with_capacity(auth.header_len());
+                    auth.write(&mut buffer).unwrap();
+                    buffer
+                };
+                let slice = Ipv4ExtensionsSlice{
+                    auth: Some(
+                        IpAuthHeaderSlice::from_slice(&buffer).unwrap()
+                    ),
+                };
+                assert_eq!(
+                    slice.to_header(),
+                    Ipv4Extensions{
+                        auth: Some(auth.clone()),
+                    }
+                );
+            }
+        }
+    }
+
+    #[test]
+    fn is_empty() {
+        // empty
+        assert!(Ipv4ExtensionsSlice { auth: None }.is_empty());
+
+        // auth
+        {
+            let buffer = {
+                let auth = IpAuthHeader::new(ip_number::UDP, 0, 0, &[]).unwrap();
+                let mut buffer = Vec::with_capacity(auth.header_len());
+                auth.write(&mut buffer).unwrap();
+                buffer
+            };
+            assert_eq!(
+                false,
+                Ipv4ExtensionsSlice {
+                    auth: Some(IpAuthHeaderSlice::from_slice(&buffer).unwrap()),
+                }
+                .is_empty()
+            );
+        }
+    }
+}
diff --git a/src/net/ipv4_header.rs b/src/net/ipv4_header.rs
new file mode 100644
index 0000000..b29e769
--- /dev/null
+++ b/src/net/ipv4_header.rs
@@ -0,0 +1,1437 @@
+use crate::{
+    err::{ValueTooBigError, ValueType},
+    *,
+};
+use arrayvec::ArrayVec;
+
+/// IPv4 header with options.
+///
+/// # Example Usage:
+///
+/// ```
+/// use etherparse::{Ipv4Header, IpNumber};
+///
+/// let mut header = Ipv4Header {
+///     source: [1,2,3,4],
+///     destination: [1,2,3,4],
+///     time_to_live: 4,
+///     total_len: Ipv4Header::MIN_LEN as u16 + 100,
+///     protocol: IpNumber::UDP,
+///     ..Default::default()
+/// };
+///
+/// // depending on your usecase you might want to set the correct checksum
+/// header.header_checksum = header.calc_header_checksum();
+///
+/// // header can be serialized into the "on the wire" format
+/// // using the "write" or "to_bytes" methods
+/// let bytes = header.to_bytes();
+///
+/// // IPv4 headers can be decoded via "read" or "from_slice"
+/// let (decoded, slice_rest) = Ipv4Header::from_slice(&bytes).unwrap();
+/// assert_eq!(header, decoded);
+/// assert_eq!(slice_rest, &[]);
+/// ```
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub struct Ipv4Header {
+    /// Differentiated Services Code Point
+    pub dscp: Ipv4Dscp,
+    /// Explicit Congestion Notification
+    pub ecn: Ipv4Ecn,
+    /// Total length of the IPv4 header (including extension headers) and the payload after it.
+    pub total_len: u16,
+    /// Number used to identify packets that contain an originally fragmented packet.
+    pub identification: u16,
+    /// If set the packet is not allowed to fragmented.
+    pub dont_fragment: bool,
+    /// Indicates that the packet contains part of an fragmented message and that
+    /// additional data is needed to reconstruct the original packet.
+    pub more_fragments: bool,
+    /// In case this message contains parts of a fragmented packet the fragment
+    /// offset is the offset of payload the current message relative to the
+    /// original payload of the message.
+    pub fragment_offset: IpFragOffset,
+    /// Number of hops the packet is allowed to take before it should be discarded.
+    pub time_to_live: u8,
+    /// IP protocol number specifying the next header or transport layer protocol.
+    ///
+    /// See [IpNumber] or [ip_number] for a definitions of ids.
+    pub protocol: IpNumber,
+    pub header_checksum: u16,
+    /// IPv4 source address
+    pub source: [u8; 4],
+    /// IPv4 destination address
+    pub destination: [u8; 4],
+    /// Options in the header (in raw).
+    pub options: Ipv4Options,
+}
+
+impl Ipv4Header {
+    /// Minimum length of an IPv4 header in bytes/octets.
+    pub const MIN_LEN: usize = 20;
+
+    /// Minimum length of an IPv4 header in bytes/octets as an `u16`.
+    pub const MIN_LEN_U16: u16 = 20;
+
+    /// Maximum length of an IPv4 header in bytes/octets.
+    ///
+    /// This number is calculated by taking the maximum value
+    /// that the "internet header length" field supports (0xf,
+    /// as the field is only 4 bits long) and multiplying it
+    /// with 4 as the "internet header length" specifies how
+    /// many 4 bytes words are present in the header.
+    pub const MAX_LEN: usize = 0b1111 * 4;
+
+    /// Deprecated use [`Ipv4Header::MIN_LEN`] instead.
+    #[deprecated(since = "0.14.0", note = "Use `Ipv4Header::MIN_LEN` instead")]
+    pub const SERIALIZED_SIZE: usize = Ipv4Header::MIN_LEN;
+
+    /// Constructs an Ipv4Header with standard values for non specified values.
+    ///
+    /// This method is equivalent to partially initializing a struct with
+    /// default values:
+    ///
+    /// ```
+    /// use etherparse::{Ipv4Header, IpNumber};
+    ///
+    /// let mut header = Ipv4Header::new(100, 4, IpNumber::UDP, [1,2,3,4], [5,6,7,8]).unwrap();
+    ///
+    /// assert_eq!(
+    ///     header,
+    ///     Ipv4Header {
+    ///         total_len: (100 + Ipv4Header::MIN_LEN) as u16,
+    ///         time_to_live: 4,
+    ///         protocol: IpNumber::UDP,
+    ///         source: [1,2,3,4],
+    ///         destination: [5,6,7,8],
+    ///         ..Default::default()
+    ///     }
+    /// );
+    ///
+    /// // for the rest of the fields the following default values will be used:
+    /// assert_eq!(0, header.dscp.value());
+    /// assert_eq!(0, header.ecn.value());
+    /// assert_eq!(0, header.identification);
+    /// assert_eq!(true, header.dont_fragment);
+    /// assert_eq!(false, header.more_fragments);
+    /// assert_eq!(0, header.fragment_offset.value());
+    /// assert_eq!(0, header.header_checksum);
+    ///
+    /// // in case you also want to have a correct checksum you will have to
+    /// // additionally update it:
+    /// header.header_checksum = header.calc_header_checksum();
+    /// ```
+    pub fn new(
+        payload_len: u16,
+        time_to_live: u8,
+        protocol: IpNumber,
+        source: [u8; 4],
+        destination: [u8; 4],
+    ) -> Result<Ipv4Header, ValueTooBigError<u16>> {
+        const MAX_PAYLOAD: u16 = u16::MAX - (Ipv4Header::MIN_LEN as u16);
+        if payload_len > MAX_PAYLOAD {
+            Err(ValueTooBigError {
+                actual: payload_len,
+                max_allowed: MAX_PAYLOAD,
+                value_type: ValueType::Ipv4PayloadLength,
+            })
+        } else {
+            Ok(Ipv4Header {
+                dscp: Default::default(),
+                ecn: Default::default(),
+                total_len: payload_len + (Ipv4Header::MIN_LEN as u16),
+                identification: 0,
+                dont_fragment: true,
+                more_fragments: false,
+                fragment_offset: Default::default(),
+                time_to_live,
+                protocol,
+                header_checksum: 0,
+                source,
+                destination,
+                options: Default::default(),
+            })
+        }
+    }
+
+    /// Length of the header in multiples of 4 bytes (often also called
+    /// IHL - Internet Header length). This field is part of the serialized
+    /// header and determines / is determined by the byte length of the options.
+    ///
+    /// The minimum allowed length of a header is 5 (= 20 bytes) and the
+    /// maximum length is 15 (= 60 bytes).
+    ///
+    /// ```
+    /// use etherparse::Ipv4Header;
+    /// {
+    ///     let header = Ipv4Header {
+    ///         options: [].into(),
+    ///         ..Default::default()
+    ///     };
+    ///     // minimum IHL is 5
+    ///     assert_eq!(5, header.ihl());
+    /// }
+    /// {
+    ///     let header = Ipv4Header {
+    ///         options: [1,2,3,4].into(),
+    ///         ..Default::default()
+    ///     };
+    ///     // IHL is increased by 1 for every 4 bytes of options
+    ///     assert_eq!(6, header.ihl());
+    /// }
+    /// {
+    ///     let header = Ipv4Header {
+    ///         options: [0;40].into(),
+    ///         ..Default::default()
+    ///     };
+    ///     // maximum ihl
+    ///     assert_eq!(15, header.ihl());
+    /// }
+    /// ```
+    #[inline]
+    pub fn ihl(&self) -> u8 {
+        (self.options.len_u8() / 4) + 5
+    }
+
+    /// Length of the header (includes options) in bytes.
+    ///
+    /// The minimum allowed length of a header is 5 (= 20 bytes) and the
+    /// maximum length is 15 (= 60 bytes).
+    ///
+    /// ```
+    /// use etherparse::Ipv4Header;
+    /// {
+    ///     let header = Ipv4Header {
+    ///         options: [].into(),
+    ///         ..Default::default()
+    ///     };
+    ///     // minimum IHL is 5
+    ///     assert_eq!(5, header.ihl());
+    /// }
+    /// {
+    ///     let header = Ipv4Header {
+    ///         options: [1,2,3,4].into(),
+    ///         ..Default::default()
+    ///     };
+    ///     // IHL is increased by 1 for every 4 bytes of options
+    ///     assert_eq!(6, header.ihl());
+    /// }
+    /// {
+    ///     let header = Ipv4Header {
+    ///         options: [0;40].into(),
+    ///         ..Default::default()
+    ///     };
+    ///     // maximum ihl
+    ///     assert_eq!(15, header.ihl());
+    /// }
+    /// ```
+    #[inline]
+    pub fn header_len(&self) -> usize {
+        Ipv4Header::MIN_LEN + self.options.len()
+    }
+
+    /// Determine the payload length based on the ihl & total_length
+    /// field of the header.
+    ///
+    /// # Example Usage
+    ///
+    /// ```
+    /// use etherparse::{Ipv4Header, Ipv4HeaderSlice};
+    ///
+    /// let header = Ipv4Header{
+    ///     // the payload len will be calculated by subtracting the
+    ///     // header length from the total length
+    ///     total_len: Ipv4Header::MIN_LEN as u16 + 100,
+    ///     ..Default::default()
+    /// };
+    ///
+    /// assert_eq!(Ok(100), header.payload_len());
+    ///
+    /// // error case
+    /// let bad_header = Ipv4Header {
+    ///     // total len should also include the header, in case it does
+    ///     // not it is not possible to calculate the payload length
+    ///     total_len: Ipv4Header::MIN_LEN as u16 - 1,
+    ///     ..Default::default()
+    /// };
+    ///
+    /// // in case the total_len is smaller then the header itself an
+    /// // error is returned
+    /// use etherparse::{LenSource, err::{LenError, Layer}};
+    /// assert_eq!(
+    ///     bad_header.payload_len(),
+    ///     Err(LenError {
+    ///         required_len: Ipv4Header::MIN_LEN,
+    ///         len: Ipv4Header::MIN_LEN - 1,
+    ///         len_source: LenSource::Ipv4HeaderTotalLen,
+    ///         layer: Layer::Ipv4Packet,
+    ///         layer_start_offset: 0,
+    ///     })
+    /// );
+    /// ```
+    #[inline]
+    pub fn payload_len(&self) -> Result<u16, err::LenError> {
+        // SAFETY: header_len() can be at most be 60 so a cast to u16 is safe.
+        let header_len = self.header_len() as u16;
+        if header_len <= self.total_len {
+            Ok(self.total_len - header_len)
+        } else {
+            use err::{Layer, LenError};
+            Err(LenError {
+                required_len: header_len.into(),
+                len: self.total_len.into(),
+                len_source: LenSource::Ipv4HeaderTotalLen,
+                layer: Layer::Ipv4Packet,
+                layer_start_offset: 0,
+            })
+        }
+    }
+
+    /// Tries setting the [`Ipv4Header::total_len`] field given the length of
+    /// the payload after the header & the current options length of the header.
+    ///
+    /// If the value is not too big. Otherwise an error is returned.
+    ///
+    /// Note that the set payload length is no longer valid if you change
+    /// [`Ipv4Header::options`] length after calling [`Ipv4Header::set_payload_len`]
+    /// as it uses  the length of options to calculate the `total_len` value.
+    ///
+    /// # Example Usage:
+    ///
+    /// ```
+    /// use etherparse::Ipv4Header;
+    ///
+    /// let mut header = Ipv4Header{
+    ///     total_len: 100, // will be reset by set_payload
+    ///     options: [1,2,3,4].into(),
+    ///     ..Default::default()
+    /// };
+    ///
+    /// // set_payload_len set the total_len field based on the header_len
+    /// // and given payload length
+    /// header.set_payload_len(100).unwrap();
+    /// assert_eq!(100 + header.header_len() as u16, header.total_len);
+    ///
+    /// // in case the payload is len is bigger then can represented in the
+    /// // total_len field an error is returned
+    /// use etherparse::err::{ValueTooBigError, ValueType};
+    /// let err = header.set_payload_len(usize::from(u16::MAX) - header.header_len() + 1);
+    /// assert_eq!(
+    ///     err,
+    ///     Err(ValueTooBigError {
+    ///         actual: usize::from(u16::MAX) - header.header_len() + 1,
+    ///         max_allowed: usize::from(u16::MAX) - header.header_len(),
+    ///         value_type: ValueType::Ipv4PayloadLength
+    ///     })
+    /// );
+    /// ```
+    pub fn set_payload_len(&mut self, value: usize) -> Result<(), ValueTooBigError<usize>> {
+        let max_allowed = usize::from(self.max_payload_len());
+        if value > max_allowed {
+            Err(ValueTooBigError {
+                actual: value,
+                max_allowed,
+                value_type: ValueType::Ipv4PayloadLength,
+            })
+        } else {
+            self.total_len = (self.header_len() + value) as u16;
+            Ok(())
+        }
+    }
+
+    /// Returns the maximum payload size based on the current options size.
+    #[inline]
+    pub fn max_payload_len(&self) -> u16 {
+        u16::MAX - u16::from(self.options.len_u8()) - (Ipv4Header::MIN_LEN as u16)
+    }
+
+    /// Returns a slice to the options part of the header (empty if no options are present).
+    #[deprecated(
+        since = "0.14.0",
+        note = "Directly use `&(header.options[..])` instead."
+    )]
+    pub fn options(&self) -> &[u8] {
+        &self.options[..]
+    }
+
+    /// Sets the options & header_length based on the provided length.
+    /// The length of the given slice must be a multiple of 4 and maximum 40 bytes.
+    /// If the length is not fulfilling these constraints, no data is set and
+    /// an error is returned.
+    #[deprecated(
+        since = "0.14.0",
+        note = "Directly set it via the header.options field instead."
+    )]
+    pub fn set_options(&mut self, data: &[u8]) -> Result<(), err::ipv4::BadOptionsLen> {
+        self.options = data.try_into()?;
+        Ok(())
+    }
+
+    /// Renamed to `Ipv4Header::from_slice`
+    #[deprecated(since = "0.10.1", note = "Renamed to `Ipv4Header::from_slice`")]
+    #[inline]
+    pub fn read_from_slice(
+        slice: &[u8],
+    ) -> Result<(Ipv4Header, &[u8]), err::ipv4::HeaderSliceError> {
+        Ipv4Header::from_slice(slice)
+    }
+
+    /// 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
+    /// `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
+    /// headers use
+    ///
+    /// * [`IpHeaders::from_ipv4_slice`] (decodes all the fields of the IP headers)
+    /// * [`Ipv4Slice::from_slice`] (just identifies the ranges in the slice where
+    ///   the headers and payload are present)
+    ///
+    /// or
+    ///
+    /// * [`IpHeaders::from_ipv4_slice_lax`]
+    /// * [`LaxIpv4Slice::from_slice`]
+    ///
+    /// for a laxer version which falls back to slice length when the `total_length`
+    /// contains an inconsistent value.
+    pub fn from_slice(slice: &[u8]) -> Result<(Ipv4Header, &[u8]), err::ipv4::HeaderSliceError> {
+        let header = Ipv4HeaderSlice::from_slice(slice)?.to_header();
+        let rest = &slice[header.header_len()..];
+        Ok((header, rest))
+    }
+
+    /// Reads an IPv4 header from the current position (requires
+    /// crate feature `std`).
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut T,
+    ) -> Result<Ipv4Header, err::ipv4::HeaderReadError> {
+        use err::ipv4::HeaderReadError::*;
+
+        let mut first_byte: [u8; 1] = [0; 1];
+        reader.read_exact(&mut first_byte).map_err(Io)?;
+
+        let version_number = first_byte[0] >> 4;
+        if 4 != version_number {
+            use err::ipv4::HeaderError::UnexpectedVersion;
+            return Err(Content(UnexpectedVersion { version_number }));
+        }
+        Ipv4Header::read_without_version(reader, first_byte[0])
+    }
+
+    /// Reads an IPv4 header assuming the version & ihl field have already
+    /// been read (requires crate feature `std`).
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read_without_version<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut T,
+        first_byte: u8,
+    ) -> Result<Ipv4Header, err::ipv4::HeaderReadError> {
+        use err::ipv4::HeaderError::*;
+        use err::ipv4::HeaderReadError::*;
+
+        // read the basic ipv4 header (the header options can be
+        // read only after the internet header length was read)
+        let mut header_raw = [0u8; 20];
+        header_raw[0] = first_byte;
+        reader.read_exact(&mut header_raw[1..]).map_err(Io)?;
+
+        let ihl = header_raw[0] & 0xf;
+
+        // validate that the internet header length is big enough to
+        // contain a basic IPv4 header without options.
+        if ihl < 5 {
+            return Err(Content(HeaderLengthSmallerThanHeader { ihl }));
+        }
+
+        let (dscp, ecn) = {
+            let value = header_raw[1];
+            (value >> 2, value & 0b0000_0011)
+        };
+        let total_len = u16::from_be_bytes([header_raw[2], header_raw[3]]);
+        let identification = u16::from_be_bytes([header_raw[4], header_raw[5]]);
+        let (dont_fragment, more_fragments, fragments_offset) = (
+            0 != (header_raw[6] & 0b0100_0000),
+            0 != (header_raw[6] & 0b0010_0000),
+            u16::from_be_bytes([header_raw[6] & 0b0001_1111, header_raw[7]]),
+        );
+        Ok(Ipv4Header {
+            dscp: unsafe {
+                // Safe as only 6 bits were used to decode the
+                // dscp value
+                Ipv4Dscp::new_unchecked(dscp)
+            },
+            ecn: unsafe {
+                // Safe as only 2 bits were used to decode the
+                // ecn value
+                Ipv4Ecn::new_unchecked(ecn)
+            },
+            total_len,
+            identification,
+            dont_fragment,
+            more_fragments,
+            fragment_offset: unsafe {
+                // Safe as only 13 bits were used to decode the
+                // fragment offset
+                IpFragOffset::new_unchecked(fragments_offset)
+            },
+            time_to_live: header_raw[8],
+            protocol: IpNumber(header_raw[9]),
+            header_checksum: u16::from_be_bytes([header_raw[10], header_raw[11]]),
+            source: [
+                header_raw[12],
+                header_raw[13],
+                header_raw[14],
+                header_raw[15],
+            ],
+            destination: [
+                header_raw[16],
+                header_raw[17],
+                header_raw[18],
+                header_raw[19],
+            ],
+            options: {
+                let mut options = Ipv4Options::new();
+                options.len = (ihl - 5) * 4;
+                if false == options.is_empty() {
+                    reader.read_exact(options.as_mut()).map_err(Io)?;
+                }
+                options
+            },
+        })
+    }
+
+    /// Writes a given IPv4 header to the current position (this method automatically calculates
+    /// the header length and checksum).
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
+        // write with recalculations
+        self.write_ipv4_header_internal(writer, self.calc_header_checksum())
+    }
+
+    /// Writes a given IPv4 header to the current position (this method just writes the specified
+    /// checksum and does not compute it).
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn write_raw<T: std::io::Write + Sized>(
+        &self,
+        writer: &mut T,
+    ) -> Result<(), std::io::Error> {
+        self.write_ipv4_header_internal(writer, self.header_checksum)
+    }
+
+    /// Returns the serialized header (note that this method does NOT
+    /// update & calculate the checksum).
+    pub fn to_bytes(&self) -> ArrayVec<u8, { Ipv4Header::MAX_LEN }> {
+        // prep the values for copy
+        let total_len_be = self.total_len.to_be_bytes();
+        let id_be = self.identification.to_be_bytes();
+        let frag_and_flags = {
+            let frag_be: [u8; 2] = self.fragment_offset.value().to_be_bytes();
+            let flags = {
+                let mut result = 0;
+                if self.dont_fragment {
+                    result |= 64;
+                }
+                if self.more_fragments {
+                    result |= 32;
+                }
+                result
+            };
+            [flags | (frag_be[0] & 0x1f), frag_be[1]]
+        };
+        let header_checksum_be = self.header_checksum.to_be_bytes();
+
+        #[rustfmt::skip]
+        let mut header_raw: ArrayVec<u8, { Ipv4Header::MAX_LEN } > = [
+            (4 << 4) | self.ihl(),
+            (self.dscp.value() << 2) | self.ecn.value(),
+            total_len_be[0],
+            total_len_be[1],
+
+            id_be[0], id_be[1], frag_and_flags[0], frag_and_flags[1],
+
+            self.time_to_live, self.protocol.0, header_checksum_be[0], header_checksum_be[1],
+            self.source[0], self.source[1], self.source[2], self.source[3],
+
+            self.destination[0], self.destination[1], self.destination[2], self.destination[3],
+            self.options.buf[0], self.options.buf[1], self.options.buf[2], self.options.buf[3],
+
+            self.options.buf[4], self.options.buf[5], self.options.buf[6], self.options.buf[7],
+            self.options.buf[8], self.options.buf[9], self.options.buf[10], self.options.buf[11],
+
+            self.options.buf[12], self.options.buf[13], self.options.buf[14], self.options.buf[15],
+            self.options.buf[16], self.options.buf[17], self.options.buf[18], self.options.buf[19],
+
+            self.options.buf[20], self.options.buf[21], self.options.buf[22], self.options.buf[23],
+            self.options.buf[24], self.options.buf[25], self.options.buf[26], self.options.buf[27],
+
+            self.options.buf[28], self.options.buf[29], self.options.buf[30], self.options.buf[31],
+            self.options.buf[32], self.options.buf[33], self.options.buf[34], self.options.buf[35],
+
+            self.options.buf[36], self.options.buf[37], self.options.buf[38], self.options.buf[39],
+        ].into();
+
+        // SAFETY: Safe as header_len() can never exceed the maximum length of an
+        // IPv4 header which is the upper limit of the array vec.
+        unsafe {
+            header_raw.set_len(self.header_len());
+        }
+
+        header_raw
+    }
+
+    /// Write the given header with the  checksum and header length specified in the seperate arguments
+    #[cfg(feature = "std")]
+    fn write_ipv4_header_internal<T: std::io::Write>(
+        &self,
+        write: &mut T,
+        header_checksum: u16,
+    ) -> Result<(), std::io::Error> {
+        let total_len_be = self.total_len.to_be_bytes();
+        let id_be = self.identification.to_be_bytes();
+        let frag_and_flags = {
+            let frag_be: [u8; 2] = self.fragment_offset.value().to_be_bytes();
+            let flags = {
+                let mut result = 0;
+                if self.dont_fragment {
+                    result |= 64;
+                }
+                if self.more_fragments {
+                    result |= 32;
+                }
+                result
+            };
+            [flags | (frag_be[0] & 0x1f), frag_be[1]]
+        };
+        let header_checksum_be = header_checksum.to_be_bytes();
+
+        let header_raw = [
+            (4 << 4) | self.ihl(),
+            (self.dscp.value() << 2) | self.ecn.value(),
+            total_len_be[0],
+            total_len_be[1],
+            id_be[0],
+            id_be[1],
+            frag_and_flags[0],
+            frag_and_flags[1],
+            self.time_to_live,
+            self.protocol.0,
+            header_checksum_be[0],
+            header_checksum_be[1],
+            self.source[0],
+            self.source[1],
+            self.source[2],
+            self.source[3],
+            self.destination[0],
+            self.destination[1],
+            self.destination[2],
+            self.destination[3],
+        ];
+        write.write_all(&header_raw)?;
+
+        //options
+        write.write_all(&self.options)?;
+
+        //done
+        Ok(())
+    }
+
+    /// Calculate header checksum of the current ipv4 header.
+    pub fn calc_header_checksum(&self) -> u16 {
+        checksum::Sum16BitWords::new()
+            .add_2bytes([
+                (4 << 4) | self.ihl(),
+                (self.dscp.value() << 2) | self.ecn.value(),
+            ])
+            .add_2bytes(self.total_len.to_be_bytes())
+            .add_2bytes(self.identification.to_be_bytes())
+            .add_2bytes({
+                let frag_off_be = self.fragment_offset.value().to_be_bytes();
+                let flags = {
+                    let mut result = 0;
+                    if self.dont_fragment {
+                        result |= 64;
+                    }
+                    if self.more_fragments {
+                        result |= 32;
+                    }
+                    result
+                };
+                [flags | (frag_off_be[0] & 0x1f), frag_off_be[1]]
+            })
+            .add_2bytes([self.time_to_live, self.protocol.0])
+            .add_4bytes(self.source)
+            .add_4bytes(self.destination)
+            .add_slice(&self.options)
+            .ones_complement()
+            .to_be()
+    }
+
+    /// Returns true if the payload is fragmented.
+    ///
+    /// Either data is missing (more_fragments set) or there is
+    /// an fragment offset.
+    #[inline]
+    pub fn is_fragmenting_payload(&self) -> bool {
+        self.more_fragments || (0 != self.fragment_offset.value())
+    }
+}
+
+impl Default for Ipv4Header {
+    fn default() -> Ipv4Header {
+        Ipv4Header {
+            dscp: Default::default(),
+            ecn: Default::default(),
+            total_len: 0,
+            identification: 0,
+            dont_fragment: true,
+            more_fragments: false,
+            fragment_offset: Default::default(),
+            time_to_live: 0,
+            protocol: IpNumber(255),
+            header_checksum: 0,
+            source: [0; 4],
+            destination: [0; 4],
+            options: Ipv4Options::new(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{
+        err::{Layer, LenError, ValueTooBigError, ValueType},
+        test_gens::*,
+        *,
+    };
+    use alloc::{format, vec::Vec};
+    use arrayvec::ArrayVec;
+    use proptest::prelude::*;
+    use std::io::Cursor;
+
+    #[test]
+    fn default() {
+        let default: Ipv4Header = Default::default();
+        assert_eq!(5, default.ihl());
+        assert_eq!(0, default.dscp.value());
+        assert_eq!(0, default.ecn.value());
+        assert_eq!(0, default.total_len);
+        assert_eq!(0, default.identification);
+        assert_eq!(true, default.dont_fragment);
+        assert_eq!(false, default.more_fragments);
+        assert_eq!(0, default.fragment_offset.value());
+        assert_eq!(0, default.time_to_live);
+        assert_eq!(IpNumber(255), default.protocol);
+        assert_eq!(0, default.header_checksum);
+        assert_eq!([0; 4], default.source);
+        assert_eq!([0; 4], default.destination);
+        assert_eq!(&default.options[..], &[]);
+    }
+
+    proptest! {
+        #[test]
+        fn debug(input in ipv4_any()) {
+            assert_eq!(&format!("Ipv4Header {{ dscp: {:?}, ecn: {:?}, total_len: {}, identification: {}, dont_fragment: {}, more_fragments: {}, fragment_offset: {:?}, time_to_live: {}, protocol: {:?}, header_checksum: {}, source: {:?}, destination: {:?}, options: {:?} }}",
+                    input.dscp,
+                    input.ecn,
+                    input.total_len,
+                    input.identification,
+                    input.dont_fragment,
+                    input.more_fragments,
+                    input.fragment_offset,
+                    input.time_to_live,
+                    input.protocol,
+                    input.header_checksum,
+                    input.source,
+                    input.destination,
+                    input.options
+                ),
+                &format!("{:?}", input)
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn eq(a in ipv4_any(),
+              b in ipv4_any())
+        {
+            //check identity equality
+            assert!(a == a);
+            assert!(b == b);
+
+            //check every field
+            //differentiated_services_code_point
+            assert_eq!(
+                a.dscp == b.dscp,
+                a == {
+                    let mut other = a.clone();
+                    other.dscp = b.dscp;
+                    other
+                }
+            );
+            //explicit_congestion_notification
+            assert_eq!(
+                a.ecn == b.ecn,
+                a == {
+                    let mut other = a.clone();
+                    other.ecn = b.ecn;
+                    other
+                }
+            );
+            //total_len
+            assert_eq!(
+                a.total_len == b.total_len,
+                a == {
+                    let mut other = a.clone();
+                    other.total_len = b.total_len;
+                    other
+                }
+            );
+            //identification
+            assert_eq!(
+                a.identification == b.identification,
+                a == {
+                    let mut other = a.clone();
+                    other.identification = b.identification;
+                    other
+                }
+            );
+            //dont_fragment
+            assert_eq!(
+                a.dont_fragment == b.dont_fragment,
+                a == {
+                    let mut other = a.clone();
+                    other.dont_fragment = b.dont_fragment;
+                    other
+                }
+            );
+            //more_fragments
+            assert_eq!(
+                a.more_fragments == b.more_fragments,
+                a == {
+                    let mut other = a.clone();
+                    other.more_fragments = b.more_fragments;
+                    other
+                }
+            );
+            //fragments_offset
+            assert_eq!(
+                a.fragment_offset == b.fragment_offset,
+                a == {
+                    let mut other = a.clone();
+                    other.fragment_offset = b.fragment_offset;
+                    other
+                }
+            );
+            //time_to_live
+            assert_eq!(
+                a.time_to_live == b.time_to_live,
+                a == {
+                    let mut other = a.clone();
+                    other.time_to_live = b.time_to_live;
+                    other
+                }
+            );
+            //protocol
+            assert_eq!(
+                a.protocol == b.protocol,
+                a == {
+                    let mut other = a.clone();
+                    other.protocol = b.protocol;
+                    other
+                }
+            );
+            //header_checksum
+            assert_eq!(
+                a.header_checksum == b.header_checksum,
+                a == {
+                    let mut other = a.clone();
+                    other.header_checksum = b.header_checksum;
+                    other
+                }
+            );
+            //source
+            assert_eq!(
+                a.source == b.source,
+                a == {
+                    let mut other = a.clone();
+                    other.source = b.source;
+                    other
+                }
+            );
+            //destination
+            assert_eq!(
+                a.destination == b.destination,
+                a == {
+                    let mut other = a.clone();
+                    other.destination = b.destination;
+                    other
+                }
+            );
+
+            //options
+            assert_eq!(
+                a.options == b.options,
+                a == {
+                    let mut other = a.clone();
+                    other.options = b.options;
+                    other
+                }
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn hash(header in ipv4_any()) {
+            use std::collections::hash_map::DefaultHasher;
+            use core::hash::{Hash, Hasher};
+            let a = {
+                let mut hasher = DefaultHasher::new();
+                header.hash(&mut hasher);
+                hasher.finish()
+            };
+            let b = {
+                let mut hasher = DefaultHasher::new();
+                header.hash(&mut hasher);
+                hasher.finish()
+            };
+            assert_eq!(a, b);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn new(
+            source_ip in prop::array::uniform4(any::<u8>()),
+            dest_ip in prop::array::uniform4(any::<u8>()),
+            ttl in any::<u8>(),
+            ok_payload_len in 0u16..=(u16::MAX - Ipv4Header::MIN_LEN as u16),
+            err_payload_len in (u16::MAX - Ipv4Header::MIN_LEN as u16 + 1)..=u16::MAX
+        ) {
+            // ok case
+            {
+                let result = Ipv4Header::new(
+                    ok_payload_len,
+                    ttl,
+                    ip_number::UDP,
+                    source_ip,
+                    dest_ip
+                ).unwrap();
+
+                assert_eq!(result.dscp.value(), 0);
+                assert_eq!(result.ecn.value(), 0);
+                assert_eq!(result.total_len, ok_payload_len + Ipv4Header::MIN_LEN as u16);
+                assert_eq!(result.identification, 0);
+                assert_eq!(result.dont_fragment, true);
+                assert_eq!(result.more_fragments, false);
+                assert_eq!(result.fragment_offset.value(), 0);
+                assert_eq!(result.time_to_live, ttl);
+                assert_eq!(result.protocol, ip_number::UDP);
+                assert_eq!(result.header_checksum, 0);
+                assert_eq!(result.source, source_ip);
+                assert_eq!(result.destination, dest_ip);
+                assert_eq!(result.options.as_slice(), &[]);
+            }
+            // err
+            {
+                assert_eq!(
+                    Ipv4Header::new(
+                        err_payload_len,
+                        ttl,
+                        ip_number::UDP,
+                        source_ip,
+                        dest_ip
+                    ),
+                    Err(ValueTooBigError::<u16>{
+                        actual: err_payload_len,
+                        max_allowed: u16::MAX - Ipv4Header::MIN_LEN as u16,
+                        value_type: ValueType::Ipv4PayloadLength,
+                    })
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn ihl(header in ipv4_any()) {
+            assert_eq!(header.ihl(), (header.header_len() / 4) as u8);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header_len(header in ipv4_any()) {
+            assert_eq!(header.header_len(), 20 + usize::from(header.options.len()));
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn payload_len(
+            header in ipv4_any()
+        ) {
+            // ok case
+            assert_eq!(
+                header.payload_len().unwrap(),
+                header.total_len - 20 - (header.options.len() as u16)
+            );
+            // err case
+            for bad_len in 0u16..(header.header_len() as u16) {
+                let mut header = header.clone();
+                header.total_len = bad_len;
+                assert_eq!(
+                    header.payload_len().unwrap_err(),
+                    LenError{
+                        required_len: header.header_len(),
+                        len: bad_len.into(),
+                        len_source: LenSource::Ipv4HeaderTotalLen,
+                        layer: Layer::Ipv4Packet,
+                        layer_start_offset: 0
+                    }
+                );
+            }
+
+        }
+    }
+
+    #[test]
+    fn set_payload_len() {
+        let mut header = Ipv4Header::new(0, 0, ip_number::UDP, [0; 4], [0; 4]).unwrap();
+
+        //add options (to make sure they are included in the calculation)
+        header.options = [1, 2, 3, 4].into();
+
+        //zero check
+        assert!(header.set_payload_len(0).is_ok());
+        assert_eq!(header.total_len, 24);
+
+        //max check
+        const MAX: usize = (core::u16::MAX as usize) - Ipv4Header::MIN_LEN - 4;
+        assert!(header.set_payload_len(MAX).is_ok());
+        assert_eq!(header.total_len, core::u16::MAX);
+
+        const OVER_MAX: usize = MAX + 1;
+        assert_eq!(
+            header.set_payload_len(OVER_MAX),
+            Err(ValueTooBigError {
+                actual: OVER_MAX,
+                max_allowed: usize::from(u16::MAX) - header.header_len(),
+                value_type: ValueType::Ipv4PayloadLength
+            })
+        );
+    }
+
+    proptest! {
+        #[test]
+        fn max_payload_len(header in ipv4_any()) {
+            assert_eq!(header.max_payload_len(), core::u16::MAX - 20 - u16::from(header.options.len_u8()));
+        }
+    }
+
+    #[test]
+    #[allow(deprecated)]
+    fn set_options() {
+        //length of 1
+        {
+            let mut header: Ipv4Header = Default::default();
+            let options = [1, 2, 3, 4];
+            assert_eq!(header.set_options(&options), Ok(()));
+
+            assert_eq!(&options, header.options());
+            assert_eq!(24, header.header_len());
+            assert_eq!(0, header.total_len);
+            assert_eq!(6, header.ihl());
+
+            //length 0
+            assert_eq!(header.set_options(&[]), Ok(()));
+
+            assert_eq!(&options[..0], header.options());
+            assert_eq!(20, header.header_len());
+            assert_eq!(0, header.total_len);
+            assert_eq!(5, header.ihl());
+        }
+        //maximum length (40)
+        {
+            let mut header: Ipv4Header = Default::default();
+            let options = [
+                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+                24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+            ];
+            assert_eq!(header.set_options(&options), Ok(()));
+
+            assert_eq!(&options[..], header.options());
+            assert_eq!(60, header.header_len());
+            assert_eq!(0, header.total_len);
+            assert_eq!(15, header.ihl());
+        }
+        //errors
+        {
+            let buffer: [u8; 50] = [0; 50];
+            for len in &[
+                1usize, 2, 3, //unaligned
+                5, 6, 7, 41, 44, //over max
+            ] {
+                let mut header: Ipv4Header = Default::default();
+
+                //expect an error
+                use self::err::ipv4::BadOptionsLen;
+                assert_eq!(
+                    Err(BadOptionsLen { bad_len: *len }),
+                    header.set_options(&buffer[..*len])
+                );
+
+                //check value was not taken
+                assert_eq!(&buffer[..0], header.options());
+                assert_eq!(20, header.header_len());
+                assert_eq!(0, header.total_len);
+                assert_eq!(5, header.ihl());
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        #[allow(deprecated)]
+        fn read_from_slice(ref input in ipv4_any()) {
+            //serialize
+            let mut buffer: Vec<u8> = Vec::with_capacity(input.header_len());
+            input.write_raw(&mut buffer).unwrap();
+            assert_eq!(input.header_len(), buffer.len());
+
+            //deserialize (read_from_slice)
+            let result = Ipv4Header::read_from_slice(&buffer).unwrap();
+            assert_eq!(input, &result.0);
+            assert_eq!(&buffer[usize::from(input.header_len())..], result.1);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(header in ipv4_any()) {
+            use err::ipv4::HeaderError::*;
+            use err::ipv4::HeaderSliceError::*;
+
+            // ok
+            {
+                let mut buffer = ArrayVec::<u8, { Ipv4Header::MAX_LEN + 1 }>::new();
+                buffer.try_extend_from_slice(&header.to_bytes()).unwrap();
+                buffer.try_extend_from_slice(&[1]).unwrap();
+
+                let (actual_header, actual_rest) = Ipv4Header::from_slice(&buffer).unwrap();
+                assert_eq!(actual_header, header);
+                assert_eq!(actual_rest, &[1]);
+            }
+
+            // unexpected end of slice
+            {
+                let buffer = header.to_bytes();
+                for len in 0..header.header_len() {
+                    assert_eq!(
+                        Ipv4Header::from_slice(&buffer[..len]),
+                        Err(Len(err::LenError{
+                            required_len: if len < Ipv4Header::MIN_LEN {
+                                Ipv4Header::MIN_LEN
+                            } else {
+                                header.header_len()
+                            },
+                            len: len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv4Header,
+                            layer_start_offset: 0,
+                        }))
+                    );
+                }
+            }
+
+            // version error
+            for version_number in 0u8..0b1111u8 {
+                if 4 != version_number {
+                    let mut buffer = header.to_bytes();
+                    // inject the bad ihl
+                    buffer[0] = (version_number << 4) | (buffer[0] & 0b1111);
+                    // expect an error
+                    assert_eq!(
+                        Ipv4Header::from_slice(&buffer).unwrap_err(),
+                        Content(UnexpectedVersion{
+                            version_number,
+                        })
+                    );
+                }
+            }
+
+            // ihl too small error
+            for ihl in 0u8..5u8 {
+                let mut buffer = header.to_bytes();
+                // inject the bad ihl
+                buffer[0] = (4 << 4) | ihl;
+                // expect an error
+                assert_eq!(
+                    Ipv4Header::from_slice(&buffer).unwrap_err(),
+                    Content(HeaderLengthSmallerThanHeader{
+                        ihl,
+                    })
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read_and_read_without_version(header in ipv4_any()) {
+            use err::ipv4::HeaderError::*;
+            use std::io::Cursor;
+
+            // ok
+            {
+                let buffer = header.to_bytes();
+
+                // read
+                {
+                    let mut cursor = Cursor::new(&buffer);
+                    let actual_header = Ipv4Header::read(&mut cursor).unwrap();
+                    assert_eq!(actual_header, header);
+                    assert_eq!(cursor.position(), header.header_len() as u64);
+                }
+                // read_without_version
+                {
+                    let mut cursor = Cursor::new(&buffer[1..]);
+                    let actual_header = Ipv4Header::read_without_version(&mut cursor, buffer[0]).unwrap();
+                    assert_eq!(actual_header, header);
+                    assert_eq!(cursor.position(), (header.header_len() - 1) as u64);
+                }
+            }
+
+            // io error
+            {
+                let buffer = header.to_bytes();
+                for len in 0..header.header_len() {
+                    // read
+                    {
+                        let mut cursor = Cursor::new(&buffer[..len]);
+                        let err = Ipv4Header::read(&mut cursor).unwrap_err();
+                        assert!(err.io_error().is_some());
+                    }
+
+                    // read_without_version
+                    if len > 0 {
+                        let mut cursor = Cursor::new(&buffer[1..len]);
+                        let err = Ipv4Header::read_without_version(&mut cursor, buffer[0]).unwrap_err();
+                        assert!(err.io_error().is_some());
+                    }
+                }
+            }
+
+            // version error
+            for version_number in 0u8..0b1111u8 {
+                if 4 != version_number {
+                    let mut buffer = header.to_bytes();
+                    // inject the bad version number
+                    buffer[0] = (version_number << 4) | (buffer[0] & 0b1111);
+
+                    // expect an error
+                    // read
+                    {
+                        let mut cursor = Cursor::new(&buffer[..]);
+                        let err = Ipv4Header::read(&mut cursor)
+                            .unwrap_err()
+                            .content_error()
+                            .unwrap();
+                        assert_eq!(err, UnexpectedVersion{ version_number });
+                    }
+
+                    // read_without_version skipped as version is not checked
+                }
+            }
+
+            // ihl too small error
+            for ihl in 0u8..5u8 {
+                let mut buffer = header.to_bytes();
+                // inject the bad ihl
+                buffer[0] = (4 << 4) | ihl;
+                // expect an error
+                // read
+                {
+                    let mut cursor = Cursor::new(&buffer[..]);
+                    let err = Ipv4Header::read(&mut cursor)
+                        .unwrap_err()
+                        .content_error()
+                        .unwrap();
+                    assert_eq!(err, HeaderLengthSmallerThanHeader{ ihl });
+                }
+
+                // read_without_version
+                {
+                    let mut cursor = Cursor::new(&buffer[1..]);
+                    let err = Ipv4Header::read_without_version(&mut cursor, buffer[0])
+                        .unwrap_err()
+                        .content_error()
+                        .unwrap();
+                    assert_eq!(err, HeaderLengthSmallerThanHeader{ ihl });
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write(ref base_header in ipv4_any()) {
+            use std::io::Cursor;
+
+            let header = {
+                let mut header = base_header.clone();
+                // set the header checksum to something else to
+                // ensure it is calculated during the write call
+                header.header_checksum = 0;
+                header
+            };
+
+            // normal write
+            {
+                //serialize
+                let buffer = {
+                    let mut buffer: Vec<u8> = Vec::with_capacity(header.header_len());
+                    header.write(&mut buffer).unwrap();
+                    buffer
+                };
+                assert_eq!(header.header_len(), buffer.len());
+
+                //deserialize
+                let mut cursor = Cursor::new(&buffer);
+                let result = Ipv4Header::read(&mut cursor).unwrap();
+                assert_eq!(header.header_len(), cursor.position() as usize);
+
+                //check equivalence (with calculated checksum)
+                let header_with_checksum = {
+                    let mut h = header.clone();
+                    h.header_checksum = h.calc_header_checksum();
+                    h
+                };
+                assert_eq!(header_with_checksum, result);
+            }
+
+            // io error
+            for len in 0..header.header_len() {
+                let mut buffer = [0u8; Ipv4Header::MAX_LEN];
+                let mut cursor = Cursor::new(&mut buffer[..len]);
+                assert!(
+                    header.write(&mut cursor).is_err()
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write_raw(base_header in ipv4_any()) {
+            // normal write
+            {
+                //serialize
+                let buffer = {
+                    let mut buffer: Vec<u8> = Vec::with_capacity(base_header.header_len());
+                    base_header.write_raw(&mut buffer).unwrap();
+                    buffer
+                };
+                assert_eq!(base_header.header_len(), buffer.len());
+
+                // decode and check for equality
+                assert_eq!(
+                    Ipv4Header::from_slice(&buffer).unwrap().0,
+                    base_header
+                );
+            }
+
+            // io error
+            for len in 0..base_header.header_len() {
+                let mut buffer = [0u8; Ipv4Header::MAX_LEN];
+                let mut cursor = Cursor::new(&mut buffer[..len]);
+                assert!(
+                    base_header.write_raw(&mut cursor).is_err()
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_bytes(base_header in ipv4_any()) {
+            let bytes = base_header.to_bytes();
+            assert_eq!(
+                base_header,
+                Ipv4HeaderSlice::from_slice(&bytes).unwrap().to_header()
+            );
+        }
+    }
+
+    #[test]
+    fn calc_header_checksum() {
+        let base: Ipv4Header = Ipv4Header::new(
+            40,
+            4, // ttl
+            ip_number::UDP,
+            [192, 168, 1, 1],   // source
+            [212, 10, 11, 123], // destination
+        )
+        .unwrap();
+
+        //without options
+        {
+            //dont_fragment && !more_fragments
+            let header = base.clone();
+            assert_eq!(0xd582, header.calc_header_checksum());
+            // !dont_fragment && more_fragments
+            let header = {
+                let mut header = base.clone();
+                header.dont_fragment = false;
+                header.more_fragments = true;
+                header
+            };
+            assert_eq!(0xf582, header.calc_header_checksum());
+        }
+        //with options
+        {
+            let header = {
+                let mut header = base.clone();
+                header.options = [1, 2, 3, 4, 5, 6, 7, 8].into();
+                header.total_len = (header.header_len() + 32) as u16;
+                header
+            };
+            assert_eq!(0xc36e, header.calc_header_checksum());
+        }
+    }
+
+    #[test]
+    fn is_fragmenting_payload() {
+        // not fragmenting
+        {
+            let mut header: Ipv4Header = Default::default();
+            header.fragment_offset = 0.try_into().unwrap();
+            header.more_fragments = false;
+            assert_eq!(false, header.is_fragmenting_payload());
+        }
+
+        // fragmenting based on offset
+        {
+            let mut header: Ipv4Header = Default::default();
+            header.fragment_offset = 1.try_into().unwrap();
+            header.more_fragments = false;
+            assert!(header.is_fragmenting_payload());
+        }
+
+        // fragmenting based on more_fragments
+        {
+            let mut header: Ipv4Header = Default::default();
+            header.fragment_offset = 0.try_into().unwrap();
+            header.more_fragments = true;
+            assert!(header.is_fragmenting_payload());
+        }
+    }
+}
diff --git a/src/net/ipv4_header_slice.rs b/src/net/ipv4_header_slice.rs
new file mode 100644
index 0000000..25c8e70
--- /dev/null
+++ b/src/net/ipv4_header_slice.rs
@@ -0,0 +1,561 @@
+use core::slice::from_raw_parts;
+#[cfg(feature = "std")]
+use std::net::Ipv4Addr;
+
+use crate::*;
+
+/// A slice containing an ipv4 header of a network package.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct Ipv4HeaderSlice<'a> {
+    slice: &'a [u8],
+}
+
+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
+    ///
+    /// * [`crate::Ipv4Slice::from_slice`] (just identifies slice ranges)
+    /// * [`crate::IpHeaders::from_ipv4_slice`] (unpacks all fields)
+    ///
+    /// or
+    ///
+    /// * [`crate::IpHeaders::from_ipv4_slice_lax`]
+    /// * [`crate::LaxIpv4Slice::from_slice`]
+    ///
+    /// for a laxer version which falls back to slice length only when the total_length
+    /// field in the header is inconsistent.
+    pub fn from_slice(slice: &'a [u8]) -> Result<Ipv4HeaderSlice<'a>, err::ipv4::HeaderSliceError> {
+        use err::ipv4::HeaderError::*;
+        use err::ipv4::HeaderSliceError::*;
+
+        // check length
+        if slice.len() < Ipv4Header::MIN_LEN {
+            return Err(Len(err::LenError {
+                required_len: Ipv4Header::MIN_LEN,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::Ipv4Header,
+                layer_start_offset: 0,
+            }));
+        }
+
+        // read version & ihl
+        let (version_number, ihl) = unsafe {
+            let value = slice.get_unchecked(0);
+            (value >> 4, value & 0xf)
+        };
+
+        // check version
+        if 4 != version_number {
+            return Err(Content(UnexpectedVersion { version_number }));
+        }
+
+        // check that the ihl is correct
+        if ihl < 5 {
+            return Err(Content(HeaderLengthSmallerThanHeader { ihl }));
+        }
+
+        // check that the slice contains enough data for the entire header + options
+        let header_length = (usize::from(ihl)) * 4;
+        if slice.len() < header_length {
+            return Err(Len(err::LenError {
+                required_len: header_length,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::Ipv4Header,
+                layer_start_offset: 0,
+            }));
+        }
+
+        //all good
+        Ok(Ipv4HeaderSlice {
+            // SAFETY:
+            // Safe as the slice length is checked to be at least
+            // header_length or greater above.
+            slice: unsafe { from_raw_parts(slice.as_ptr(), header_length) },
+        })
+    }
+
+    /// Converts the given slice into a ipv4 header slice WITHOUT any
+    /// checks to ensure that the data present is an ipv4 header or that the
+    /// slice length is matching the header length.
+    ///
+    /// If you are not sure what this means, use [`Ipv4HeaderSlice::from_slice`]
+    /// instead.
+    ///
+    /// # Safety
+    ///
+    /// It must ensured that the slice exactly contains the IPv4 header
+    /// and the ihl (intra header length) & total length must be consistent.
+    #[inline]
+    pub(crate) unsafe fn from_slice_unchecked(slice: &[u8]) -> Ipv4HeaderSlice {
+        Ipv4HeaderSlice { slice }
+    }
+
+    /// Returns the slice containing the ipv4 header
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+
+    /// Read the "version" field of the IPv4 header (should be 4).
+    #[inline]
+    pub fn version(&self) -> u8 {
+        // SAFETY:
+        // Safe as the slice length is checked to be at least
+        // Ipv4Header::MIN_LEN (20) in the constructor.
+        unsafe { *self.slice.get_unchecked(0) >> 4 }
+    }
+
+    /// Read the "ip header length" (length of the ipv4 header + options in multiples of 4 bytes).
+    #[inline]
+    pub fn ihl(&self) -> u8 {
+        // SAFETY:
+        // Safe as the slice length is checked to be at least
+        // Ipv4Header::MIN_LEN (20) in the constructor.
+        unsafe { *self.slice.get_unchecked(0) & 0xf }
+    }
+
+    /// Read the "differentiated_services_code_point" from the slice.
+    #[inline]
+    pub fn dcp(&self) -> Ipv4Dscp {
+        // 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
+        // value is not bigger then 6 bits.
+        unsafe { Ipv4Dscp::new_unchecked(*self.slice.get_unchecked(1) >> 2) }
+    }
+
+    /// Read the "explicit_congestion_notification" from the slice.
+    #[inline]
+    pub fn ecn(&self) -> Ipv4Ecn {
+        // 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.
+        unsafe { Ipv4Ecn::new_unchecked(*self.slice.get_unchecked(1) & 0b0000_0011) }
+    }
+
+    /// Read the "total length" from the slice (total length of ip header + payload).
+    #[inline]
+    pub fn total_len(&self) -> u16 {
+        // SAFETY:
+        // Safe as the slice length is checked to be at least
+        // Ipv4Header::MIN_LEN (20) in the constructor.
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) }
+    }
+
+    /// Determine the payload length based on the ihl & total_length
+    /// field of the header.
+    ///
+    /// # Example Usage
+    ///
+    /// ```
+    /// use etherparse::{Ipv4Header, Ipv4HeaderSlice};
+    ///
+    /// let bytes = Ipv4Header{
+    ///     // the payload len will be calculated by subtracting the
+    ///     // header length from the total length
+    ///     total_len: Ipv4Header::MIN_LEN as u16 + 100,
+    ///     ..Default::default()
+    /// }.to_bytes();
+    ///
+    /// let slice = Ipv4HeaderSlice::from_slice(&bytes).unwrap();
+    /// assert_eq!(Ok(100), slice.payload_len());
+    ///
+    /// // error case
+    /// let bad_bytes = Ipv4Header {
+    ///     // total len should also include the header, in case it does
+    ///     // not it is not possible to calculate the payload length
+    ///     total_len: Ipv4Header::MIN_LEN as u16 - 1,
+    ///     ..Default::default()
+    /// }.to_bytes();
+    ///
+    /// let bad_slice = Ipv4HeaderSlice::from_slice(&bad_bytes).unwrap();
+    /// // in case the total_len is smaller then the header itself an
+    /// // error is returned
+    /// use etherparse::{err::{LenError, Layer}, LenSource};
+    /// assert_eq!(
+    ///     bad_slice.payload_len(),
+    ///     Err(LenError {
+    ///         required_len: Ipv4Header::MIN_LEN,
+    ///         len: Ipv4Header::MIN_LEN - 1,
+    ///         len_source: LenSource::Ipv4HeaderTotalLen,
+    ///         layer: Layer::Ipv4Packet,
+    ///         layer_start_offset: 0,
+    ///     })
+    /// );
+    /// ```
+    #[inline]
+    pub fn payload_len(&self) -> Result<u16, err::LenError> {
+        let total_len = self.total_len();
+        // SAFETY: slice.len() can be at most be 60 (verified in from_slice) so a
+        // cast to u16 is safe.
+        let header_len = self.slice.len() as u16;
+        if header_len <= total_len {
+            Ok(total_len - header_len)
+        } else {
+            use err::{Layer, LenError};
+            Err(LenError {
+                required_len: header_len.into(),
+                len: total_len.into(),
+                len_source: LenSource::Ipv4HeaderTotalLen,
+                layer: Layer::Ipv4Packet,
+                layer_start_offset: 0,
+            })
+        }
+    }
+
+    /// Read the "identification" field from the slice.
+    #[inline]
+    pub fn identification(&self) -> u16 {
+        // SAFETY:
+        // Safe as the slice length is checked to be at least
+        // Ipv4Header::MIN_LEN (20) in the constructor.
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(4)) }
+    }
+
+    /// Read the "dont fragment" flag from the slice.
+    #[inline]
+    pub fn dont_fragment(&self) -> bool {
+        // SAFETY:
+        // Safe as the slice length is checked to be at least
+        // Ipv4Header::MIN_LEN (20) in the constructor.
+        unsafe { 0 != (*self.slice.get_unchecked(6) & 0x40) }
+    }
+
+    /// Read the "more fragments" flag from the slice.
+    #[inline]
+    pub fn more_fragments(&self) -> bool {
+        // SAFETY:
+        // Safe as the slice length is checked to be at least
+        // Ipv4Header::MIN_LEN (20) in the constructor.
+        unsafe { 0 != (*self.slice.get_unchecked(6) & 0x20) }
+    }
+
+    /// Read the "fragment_offset" field from the slice.
+    #[inline]
+    pub fn fragments_offset(&self) -> IpFragOffset {
+        unsafe {
+            // SAFETY:
+            // Safe as the value is limited to be 13 bits long bellow.
+            IpFragOffset::new_unchecked(u16::from_be_bytes([
+                // SAFETY:
+                // Safe as the slice length is checked to be at least
+                // Ipv4Header::MIN_LEN (20) in the constructor.
+                *self.slice.get_unchecked(6) & 0x1f,
+                *self.slice.get_unchecked(7),
+            ]))
+        }
+    }
+
+    /// Read the "time_to_live" field from the slice.
+    #[inline]
+    pub fn ttl(&self) -> u8 {
+        // SAFETY:
+        // Safe as the slice length is checked to be at least
+        // Ipv4Header::MIN_LEN (20) in the constructor.
+        unsafe { *self.slice.get_unchecked(8) }
+    }
+
+    /// Read the "protocol" field from the slice.
+    #[inline]
+    pub fn protocol(&self) -> IpNumber {
+        // SAFETY:
+        // Safe as the slice length is checked to be at least
+        // Ipv4Header::MIN_LEN (20) in the constructor.
+        IpNumber(unsafe { *self.slice.get_unchecked(9) })
+    }
+
+    /// Read the "header checksum" field from the slice.
+    #[inline]
+    pub fn header_checksum(&self) -> u16 {
+        // SAFETY:
+        // Safe as the slice length is checked to be at least
+        // Ipv4Header::MIN_LEN (20) in the constructor.
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(10)) }
+    }
+
+    /// Returns a slice containing the ipv4 source address.
+    #[inline]
+    pub fn source(&self) -> [u8; 4] {
+        // SAFETY:
+        // Safe as the slice length is checked to be at least
+        // Ipv4Header::MIN_LEN (20) in the constructor.
+        unsafe { get_unchecked_4_byte_array(self.slice.as_ptr().add(12)) }
+    }
+
+    /// Return the ipv4 source address as an std::net::Ipv4Addr
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    #[inline]
+    pub fn source_addr(&self) -> Ipv4Addr {
+        Ipv4Addr::from(self.source())
+    }
+
+    /// Returns a slice containing the ipv4 source address.
+    #[inline]
+    pub fn destination(&self) -> [u8; 4] {
+        // SAFETY:
+        // Safe as the slice length is checked to be at least
+        // Ipv4Header::MIN_LEN (20) in the constructor.
+        unsafe { get_unchecked_4_byte_array(self.slice.as_ptr().add(16)) }
+    }
+
+    /// Return the ipv4 destination address as an std::net::Ipv4Addr
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    #[inline]
+    pub fn destination_addr(&self) -> Ipv4Addr {
+        Ipv4Addr::from(self.destination())
+    }
+
+    /// Returns a slice containing the ipv4 header options (empty when there are no options).
+    #[inline]
+    pub fn options(&self) -> &'a [u8] {
+        // SAFETY:
+        // Safe as the slice length is checked to be at least
+        // Ipv4Header::MIN_LEN (20) in the constructor.
+        unsafe { from_raw_parts(self.slice.as_ptr().add(20), self.slice.len() - 20) }
+    }
+
+    /// Returns true if the payload is fragmented.
+    ///
+    /// Either data is missing (more_fragments set) or there is
+    /// an fragment offset.
+    #[inline]
+    pub fn is_fragmenting_payload(&self) -> bool {
+        self.more_fragments() || (0 != self.fragments_offset().value())
+    }
+
+    /// Decode all the fields and copy the results to a Ipv4Header struct
+    #[inline]
+    pub fn to_header(&self) -> Ipv4Header {
+        Ipv4Header {
+            dscp: self.dcp(),
+            ecn: self.ecn(),
+            total_len: self.total_len(),
+            identification: self.identification(),
+            dont_fragment: self.dont_fragment(),
+            more_fragments: self.more_fragments(),
+            fragment_offset: self.fragments_offset(),
+            time_to_live: self.ttl(),
+            protocol: self.protocol(),
+            header_checksum: self.header_checksum(),
+            source: self.source(),
+            destination: self.destination(),
+            options: {
+                let options_slice = self.options();
+                let mut options = Ipv4Options::new();
+                options.len = options_slice.len() as u8;
+                let target_slice: &mut [u8] = options.as_mut();
+                target_slice.copy_from_slice(options_slice);
+                options
+            },
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{test_gens::*, *};
+    use alloc::{format, vec::Vec};
+    use arrayvec::ArrayVec;
+    use proptest::prelude::*;
+
+    #[test]
+    fn debug() {
+        let buffer = {
+            let header: Ipv4Header = Default::default();
+            header.to_bytes()
+        };
+        let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
+        assert_eq!(
+            format!("{:?}", slice),
+            format!("Ipv4HeaderSlice {{ slice: {:?} }}", slice.slice())
+        );
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(header in ipv4_any()) {
+            let buffer = header.to_bytes();
+            let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(slice.clone(), slice);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(header in ipv4_any()) {
+            use err::ipv4::HeaderError::*;
+            use err::ipv4::HeaderSliceError::*;
+
+            // ok
+            {
+                let mut buffer = ArrayVec::<u8, { Ipv4Header::MAX_LEN + 1 }>::new();
+                buffer.try_extend_from_slice(&header.to_bytes()).unwrap();
+                buffer.try_extend_from_slice(&[1]).unwrap();
+
+                let actual_slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
+                assert_eq!(actual_slice.to_header(), header);
+                assert_eq!(actual_slice.slice(), &buffer[..header.header_len()]);
+            }
+
+            // unexpected end of slice
+            {
+                let buffer = header.to_bytes();
+                for len in 0..header.header_len() {
+                    assert_eq!(
+                        Ipv4HeaderSlice::from_slice(&buffer[..len]),
+                        Err(Len(err::LenError{
+                            required_len: if len < Ipv4Header::MIN_LEN {
+                                Ipv4Header::MIN_LEN
+                            } else {
+                                header.header_len()
+                            },
+                            len: len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv4Header,
+                            layer_start_offset: 0,
+                        }))
+                    );
+                }
+            }
+
+            // version error
+            for version_number in 0u8..0b1111u8 {
+                if 4 != version_number {
+                    let mut buffer = header.to_bytes();
+                    // inject the bad ihl
+                    buffer[0] = (version_number << 4) | (buffer[0] & 0b1111);
+                    // expect an error
+                    assert_eq!(
+                        Ipv4HeaderSlice::from_slice(&buffer).unwrap_err(),
+                        Content(UnexpectedVersion{
+                            version_number,
+                        })
+                    );
+                }
+            }
+
+            // ihl too small error
+            for ihl in 0u8..5u8 {
+                let mut buffer = header.to_bytes();
+                // inject the bad ihl
+                buffer[0] = (4 << 4) | ihl;
+                // expect an error
+                assert_eq!(
+                    Ipv4HeaderSlice::from_slice(&buffer).unwrap_err(),
+                    Content(HeaderLengthSmallerThanHeader{
+                        ihl,
+                    })
+                );
+            }
+        }
+    }
+
+    #[test]
+    fn from_slice_unchecked() {
+        let buffer = [0u8; 4];
+        let slice = unsafe { Ipv4HeaderSlice::from_slice_unchecked(&buffer) };
+        assert_eq!(slice.slice(), &buffer);
+    }
+
+    proptest! {
+        #[test]
+        fn getters(header in ipv4_any()) {
+            let buffer = header.to_bytes();
+            let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
+
+            assert_eq!(slice.slice(), &buffer[..]);
+            assert_eq!(slice.version(), 4);
+            assert_eq!(slice.ihl(), header.ihl());
+            assert_eq!(slice.dcp(), header.dscp);
+            assert_eq!(slice.ecn(), header.ecn);
+            assert_eq!(slice.total_len(), header.total_len);
+            assert_eq!(slice.payload_len(), header.payload_len());
+            assert_eq!(slice.identification(), header.identification);
+            assert_eq!(slice.dont_fragment(), header.dont_fragment);
+            assert_eq!(slice.more_fragments(), header.more_fragments);
+            assert_eq!(slice.fragments_offset(), header.fragment_offset);
+            assert_eq!(slice.ttl(), header.time_to_live);
+            assert_eq!(slice.protocol(), header.protocol);
+            assert_eq!(slice.header_checksum(), header.header_checksum);
+            assert_eq!(slice.source(), header.source);
+            assert_eq!(slice.destination(), header.destination);
+            assert_eq!(slice.options(), &header.options[..]);
+        }
+    }
+
+    #[cfg(feature = "std")]
+    proptest! {
+        #[test]
+        fn getters_std(header in ipv4_any()) {
+            use std::net::Ipv4Addr;
+
+            let buffer = header.to_bytes();
+            let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
+
+            assert_eq!(slice.source_addr(), Ipv4Addr::from(header.source));
+            assert_eq!(slice.destination_addr(), Ipv4Addr::from(header.destination));
+        }
+    }
+
+    #[test]
+    fn is_fragmenting_payload() {
+        // not fragmenting
+        {
+            let buffer = {
+                let mut header: Ipv4Header = Default::default();
+                header.fragment_offset = 0.try_into().unwrap();
+                header.more_fragments = false;
+                let mut buffer = Vec::with_capacity(header.header_len());
+                header.write(&mut buffer).unwrap();
+                buffer
+            };
+            let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(false, slice.is_fragmenting_payload());
+        }
+
+        // fragmenting based on offset
+        {
+            let buffer = {
+                let mut header: Ipv4Header = Default::default();
+                header.fragment_offset = 1.try_into().unwrap();
+                header.more_fragments = false;
+                let mut buffer = Vec::with_capacity(header.header_len());
+                header.write(&mut buffer).unwrap();
+                buffer
+            };
+            let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
+            assert!(slice.is_fragmenting_payload());
+        }
+
+        // fragmenting based on more_fragments
+        {
+            let buffer = {
+                let mut header: Ipv4Header = Default::default();
+                header.fragment_offset = 0.try_into().unwrap();
+                header.more_fragments = true;
+                let mut buffer = Vec::with_capacity(header.header_len());
+                header.write(&mut buffer).unwrap();
+                buffer
+            };
+            let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
+            assert!(slice.is_fragmenting_payload());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_header(header in ipv4_any()) {
+            let buffer = header.to_bytes();
+            let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(slice.to_header(), header);
+        }
+    }
+}
diff --git a/src/net/ipv4_options.rs b/src/net/ipv4_options.rs
new file mode 100644
index 0000000..f741a07
--- /dev/null
+++ b/src/net/ipv4_options.rs
@@ -0,0 +1,493 @@
+use core::borrow::{Borrow, BorrowMut};
+
+/// Options present in an [`crate::Ipv4Header`].
+///
+/// IPv4 header options can only have a length that
+/// is a multiple of 4 bytes (meaning 4, 8, 12, ...) and
+/// a maximum length of 40 bytes (40 bytes the maximum length is
+/// limited by maximum value of the "intra header length" field
+/// in the IPv4 header).
+///
+/// # Examples
+///
+/// ```
+/// use etherparse::Ipv4Options;
+///
+/// {
+///     // static sized arrays of size 4,8,... 40 can directly be converted
+///     let options: Ipv4Options = [1,2,3,4].into();
+///     assert_eq!(&options[..], &[1,2,3,4]);
+/// }
+///
+/// {
+///     // slices can also be "try_from" converted
+///     let some_data = vec![1,2,3,4,5,6,7,8];
+///     let options: Ipv4Options = (&some_data[..]).try_into().unwrap();
+///     assert_eq!(options.as_slice(), &[1,2,3,4,5,6,7,8]);
+/// }
+/// {
+///     // only slices with a length that is multiple of 4 and a maximum value of 40
+///     // can be converted, otherwise you will get an error
+///     use etherparse::err::ipv4::BadOptionsLen;
+///
+///     let result = Ipv4Options::try_from(&[1,2,3][..]);
+///     assert_eq!(result, Err(BadOptionsLen { bad_len: 3 }));
+/// }
+/// ```
+#[derive(Clone)]
+pub struct Ipv4Options {
+    pub(crate) len: u8,
+    pub(crate) buf: [u8; 40],
+}
+
+impl Ipv4Options {
+    /// Maximum length of the IPv4 options in bytes.
+    pub const MAX_LEN: u8 = 40;
+
+    /// Setup an empty options array.
+    #[inline]
+    pub fn new() -> Ipv4Options {
+        Ipv4Options {
+            len: 0,
+            buf: [0; 40],
+        }
+    }
+
+    /// Returns the slice containing the data of the options.
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        unsafe { core::slice::from_raw_parts(self.buf.as_ptr(), self.len.into()) }
+    }
+
+    /// Returns a mutable slice containing the data of the options.
+    #[inline]
+    pub fn as_mut_slice(&mut self) -> &mut [u8] {
+        unsafe { core::slice::from_raw_parts_mut(self.buf.as_mut_ptr(), self.len.into()) }
+    }
+
+    /// Returns the length of the options in bytes.
+    #[inline]
+    pub fn len(&self) -> usize {
+        usize::from(self.len)
+    }
+
+    /// Returns the length of the options in bytes.
+    #[inline]
+    pub fn len_u8(&self) -> u8 {
+        self.len
+    }
+
+    /// Returns if the length of the options is zero.
+    #[inline]
+    pub fn is_empty(&self) -> bool {
+        self.len == 0
+    }
+}
+
+impl TryFrom<&[u8]> for Ipv4Options {
+    type Error = crate::err::ipv4::BadOptionsLen;
+
+    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
+        if value.len() <= 40 && value.len() % 4 == 0 {
+            let mut result = Ipv4Options {
+                len: value.len() as u8,
+                buf: [0; 40],
+            };
+            unsafe {
+                // SAFETY: Safe as value.len() <= 40 and the result buffer size is 40.
+                core::ptr::copy_nonoverlapping(
+                    value.as_ptr(),
+                    result.buf.as_mut_ptr(),
+                    value.len(),
+                );
+            }
+            Ok(result)
+        } else {
+            Err(Self::Error {
+                bad_len: value.len(),
+            })
+        }
+    }
+}
+
+impl Default for Ipv4Options {
+    #[inline]
+    fn default() -> Self {
+        Self {
+            len: 0,
+            buf: [0; 40],
+        }
+    }
+}
+
+impl core::fmt::Debug for Ipv4Options {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        self.as_slice().fmt(f)
+    }
+}
+
+impl PartialEq for Ipv4Options {
+    fn eq(&self, other: &Self) -> bool {
+        self.as_slice() == other.as_slice()
+    }
+}
+impl Eq for Ipv4Options {}
+
+impl core::hash::Hash for Ipv4Options {
+    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+        self.as_slice().hash(state);
+    }
+}
+
+impl core::cmp::PartialOrd for Ipv4Options {
+    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+        Some(self.as_slice().cmp(other.as_slice()))
+    }
+}
+
+impl core::cmp::Ord for Ipv4Options {
+    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+        self.as_slice().cmp(other.as_slice())
+    }
+}
+
+impl From<[u8; 0]> for Ipv4Options {
+    #[inline]
+    fn from(_: [u8; 0]) -> Self {
+        Ipv4Options {
+            len: 0,
+            buf: [0; 40],
+        }
+    }
+}
+
+macro_rules! from_static_array {
+    ($x:expr) => {
+        impl From<[u8; $x]> for Ipv4Options {
+            #[inline]
+            fn from(values: [u8; $x]) -> Self {
+                let mut result = Ipv4Options {
+                    len: $x,
+                    buf: [0; 40],
+                };
+                let r = result.buf.as_mut_ptr() as *mut [u8; $x];
+                unsafe {
+                    *r = values;
+                }
+                result
+            }
+        }
+    };
+}
+
+from_static_array!(4);
+from_static_array!(8);
+from_static_array!(12);
+from_static_array!(16);
+from_static_array!(20);
+from_static_array!(24);
+from_static_array!(28);
+from_static_array!(32);
+from_static_array!(36);
+
+impl From<[u8; 40]> for Ipv4Options {
+    fn from(values: [u8; 40]) -> Self {
+        Ipv4Options {
+            len: 40,
+            buf: values,
+        }
+    }
+}
+
+impl AsRef<Ipv4Options> for Ipv4Options {
+    fn as_ref(&self) -> &Ipv4Options {
+        self
+    }
+}
+
+impl AsRef<[u8]> for Ipv4Options {
+    fn as_ref(&self) -> &[u8] {
+        self.as_slice()
+    }
+}
+
+impl AsMut<Ipv4Options> for Ipv4Options {
+    fn as_mut(&mut self) -> &mut Ipv4Options {
+        self
+    }
+}
+
+impl AsMut<[u8]> for Ipv4Options {
+    fn as_mut(&mut self) -> &mut [u8] {
+        self.as_mut_slice()
+    }
+}
+
+impl Borrow<[u8]> for Ipv4Options {
+    fn borrow(&self) -> &[u8] {
+        self.as_slice()
+    }
+}
+
+impl BorrowMut<[u8]> for Ipv4Options {
+    fn borrow_mut(&mut self) -> &mut [u8] {
+        self.as_mut_slice()
+    }
+}
+
+impl core::ops::Deref for Ipv4Options {
+    type Target = [u8];
+
+    #[inline]
+    fn deref(&self) -> &[u8] {
+        self.as_slice()
+    }
+}
+
+impl core::ops::DerefMut for Ipv4Options {
+    #[inline]
+    fn deref_mut(&mut self) -> &mut [u8] {
+        self.as_mut_slice()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::test_gens::*;
+    use proptest::prelude::*;
+    use std::format;
+
+    #[test]
+    fn new() {
+        let actual = Ipv4Options::new();
+        assert_eq!(actual.len, 0);
+        assert_eq!(actual.buf, [0; 40]);
+    }
+
+    #[test]
+    fn is_empty() {
+        {
+            let actual = Ipv4Options::new();
+            assert!(actual.is_empty());
+        }
+        {
+            let actual: Ipv4Options = [1, 2, 3, 4].into();
+            assert_eq!(false, actual.is_empty());
+        }
+    }
+
+    #[test]
+    fn try_from() {
+        const DATA: [u8; 48] = [
+            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+            25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+            47, 48,
+        ];
+
+        // ok cases
+        for len_div_4 in 0usize..=10 {
+            let mut actual = Ipv4Options::try_from(&DATA[..len_div_4 * 4]).unwrap();
+            assert_eq!(actual.as_slice(), &DATA[..len_div_4 * 4]);
+            assert_eq!(actual.as_mut_slice(), &DATA[..len_div_4 * 4]);
+            assert_eq!(actual.len_u8(), (len_div_4 * 4) as u8);
+            assert_eq!(actual.len(), len_div_4 * 4);
+        }
+
+        // error cases
+        use crate::err::ipv4::BadOptionsLen;
+        for len in 0usize..48 {
+            if (len % 4 != 0) || len > 40 {
+                assert_eq!(
+                    Err(BadOptionsLen { bad_len: len }),
+                    Ipv4Options::try_from(&DATA[..len])
+                )
+            }
+        }
+    }
+
+    #[test]
+    fn default() {
+        let actual: Ipv4Options = Default::default();
+        assert_eq!(actual.len, 0);
+        assert_eq!(actual.buf, [0; 40]);
+    }
+
+    proptest! {
+        #[test]
+        fn clone_dbg(options in ipv4_options_any()) {
+            assert_eq!(
+                format!("{:?}", options),
+                format!("{:?}", options.clone().as_slice())
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn eq_partial_eq(
+            a in ipv4_options_any(),
+            b in ipv4_options_any()
+        ) {
+            assert_eq!(a.eq(&b), a.as_slice().eq(b.as_slice()));
+            assert_eq!(a == b, a.as_slice() == b.as_slice());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn hash(
+            options in ipv4_options_any()
+        ) {
+            use std::collections::hash_map::DefaultHasher;
+            use core::hash::{Hash, Hasher};
+            let a = {
+                let mut hasher = DefaultHasher::new();
+                options.hash(&mut hasher);
+                hasher.finish()
+            };
+            let b = {
+                let mut hasher = DefaultHasher::new();
+                options.hash(&mut hasher);
+                hasher.finish()
+            };
+            assert_eq!(a, b);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn ord_partial_ord(
+            a in ipv4_options_any(),
+            b in ipv4_options_any()
+        ) {
+            assert_eq!(a.cmp(&b), a.as_slice().cmp(&b.as_slice()));
+            assert_eq!(a.partial_cmp(&b), a.as_slice().partial_cmp(&b.as_slice()));
+        }
+    }
+
+    #[test]
+    fn from_0_byte_array() {
+        let options: Ipv4Options = [].into();
+        assert_eq!(&options[..], &[]);
+    }
+
+    macro_rules! from_static_array_test {
+        ($func_name:ident, $x:expr) => {
+            #[test]
+            fn $func_name() {
+                {
+                    let options: Ipv4Options = [$x; $x].into();
+                    assert_eq!(&options[..], &[$x; $x]);
+                }
+                assert_eq!(&Ipv4Options::from([$x; $x])[..], &[$x; $x]);
+            }
+        };
+    }
+
+    from_static_array_test!(from_arr_4, 4);
+    from_static_array_test!(from_arr_8, 8);
+    from_static_array_test!(from_arr_12, 12);
+    from_static_array_test!(from_arr_16, 16);
+    from_static_array_test!(from_arr_20, 20);
+    from_static_array_test!(from_arr_24, 24);
+    from_static_array_test!(from_arr_28, 28);
+    from_static_array_test!(from_arr_32, 32);
+    from_static_array_test!(from_arr_36, 36);
+    from_static_array_test!(from_arr_40, 40);
+
+    proptest! {
+        #[test]
+        fn as_ref(options in ipv4_options_any()) {
+            // as object reference
+            {
+                let r: &Ipv4Options = options.as_ref();
+                assert_eq!(r, &options);
+            }
+            // as slice reference
+            {
+                let r: &[u8] = options.as_ref();
+                assert_eq!(r, options.as_slice());
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn as_mut(options in ipv4_options_any()) {
+            // as object reference
+            {
+                let mut o = options.clone();
+                let r: &mut Ipv4Options = o.as_mut();
+                if r.len() > 0 {
+                    r[0] = 123;
+                    assert_eq!(123, o.as_slice()[0]);
+                }
+            }
+            // as slice reference
+            {
+                let mut o = options.clone();
+                let r: &mut [u8] = o.as_mut();
+                if r.len() > 0 {
+                    r[0] = 123;
+                    assert_eq!(123, o.as_slice()[0]);
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn borrow(options in ipv4_options_any()) {
+            // as object reference
+            {
+                let r: &Ipv4Options = options.borrow();
+                assert_eq!(r, &options);
+            }
+            // as slice reference
+            {
+                let r: &[u8] = options.borrow();
+                assert_eq!(r, options.as_slice());
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn borrow_mut(options in ipv4_options_any()) {
+            // as object reference
+            {
+                let mut o = options.clone();
+                let r: &mut Ipv4Options = o.borrow_mut();
+                if r.len() > 0 {
+                    r[0] = 123;
+                    assert_eq!(123, o.as_slice()[0]);
+                }
+            }
+            // as slice reference
+            {
+                let mut o = options.clone();
+                let r: &mut [u8] = o.borrow_mut();
+                if r.len() > 0 {
+                    r[0] = 123;
+                    assert_eq!(123, o.as_slice()[0]);
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn deref() {
+        let options: Ipv4Options = [1, 2, 3, 4].into();
+        let s: &[u8] = &options;
+        assert_eq!(s, &[1, 2, 3, 4]);
+        assert_eq!(&options[..], &[1, 2, 3, 4]);
+    }
+
+    #[test]
+    fn deref_mut() {
+        let mut options: Ipv4Options = [1, 2, 3, 4].into();
+        let s: &mut [u8] = &mut options;
+        assert_eq!(s, &[1, 2, 3, 4]);
+    }
+}
diff --git a/src/net/ipv4_slice.rs b/src/net/ipv4_slice.rs
new file mode 100644
index 0000000..e22a566
--- /dev/null
+++ b/src/net/ipv4_slice.rs
@@ -0,0 +1,419 @@
+use crate::{
+    err::{ipv4::SliceError, Layer, LenError},
+    *,
+};
+
+/// Slice containing the IPv4 headers & payload.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Ipv4Slice<'a> {
+    pub(crate) header: Ipv4HeaderSlice<'a>,
+    pub(crate) exts: Ipv4ExtensionsSlice<'a>,
+    pub(crate) payload: IpPayloadSlice<'a>,
+}
+
+impl<'a> Ipv4Slice<'a> {
+    /// Separates and validates IPv4 headers (including extension headers)
+    /// in the given slice and determine the sub-slice containing the payload
+    /// of the IPv4 packet.
+    ///
+    /// Note that his function returns an [`crate::err::LenError`] if the given slice
+    /// contains less data then the `total_len` field in the IPv4 header indicates
+    /// should be present.
+    ///
+    /// If you want to ignore these kind of length errors based on the length
+    /// fields in the IP headers use [`crate::LaxIpv4Slice::from_slice`] instead.
+    pub fn from_slice(slice: &[u8]) -> Result<Ipv4Slice, SliceError> {
+        use crate::ip_number::AUTH;
+
+        // decode the header
+        let header = Ipv4HeaderSlice::from_slice(slice).map_err(|err| {
+            use crate::err::ipv4::HeaderSliceError::*;
+            match err {
+                Len(err) => SliceError::Len(err),
+                Content(err) => SliceError::Header(err),
+            }
+        })?;
+
+        // validate total_len at least contains the header
+        let header_total_len: usize = header.total_len().into();
+        if header_total_len < header.slice().len() {
+            return Err(SliceError::Len(LenError {
+                required_len: header.slice().len(),
+                len: header_total_len,
+                len_source: LenSource::Ipv4HeaderTotalLen,
+                layer: Layer::Ipv4Packet,
+                layer_start_offset: 0,
+            }));
+        }
+
+        // check slice length based on the total length
+        let header_payload = if slice.len() < header_total_len {
+            return Err(SliceError::Len(LenError {
+                required_len: header_total_len,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: Layer::Ipv4Packet,
+                layer_start_offset: 0,
+            }));
+        } else {
+            unsafe {
+                core::slice::from_raw_parts(
+                    slice.as_ptr().add(header.slice().len()),
+                    header_total_len - header.slice().len(),
+                )
+            }
+        };
+
+        // decode the authentication header if needed
+        let fragmented = header.is_fragmenting_payload();
+        match header.protocol() {
+            AUTH => {
+                use crate::err::ip_auth::HeaderSliceError as E;
+
+                // parse extension headers
+                let auth = match IpAuthHeaderSlice::from_slice(header_payload) {
+                    Ok(s) => s,
+                    Err(err) => match err {
+                        E::Len(mut l) => {
+                            // change the length source to the ipv4 header
+                            l.len_source = LenSource::Ipv4HeaderTotalLen;
+                            l.layer_start_offset += header.slice().len();
+                            return Err(SliceError::Len(l));
+                        }
+                        E::Content(err) => return Err(SliceError::Exts(err)),
+                    },
+                };
+
+                // remove the extension header from the payload
+                let payload = unsafe {
+                    core::slice::from_raw_parts(
+                        header_payload.as_ptr().add(auth.slice().len()),
+                        header_payload.len() - auth.slice().len(),
+                    )
+                };
+                let ip_number = auth.next_header();
+                Ok(Ipv4Slice {
+                    header,
+                    exts: Ipv4ExtensionsSlice { auth: Some(auth) },
+                    payload: IpPayloadSlice {
+                        ip_number,
+                        fragmented,
+                        len_source: LenSource::Ipv4HeaderTotalLen,
+                        payload,
+                    },
+                })
+            }
+            ip_number => Ok(Ipv4Slice {
+                header,
+                exts: Ipv4ExtensionsSlice { auth: None },
+                payload: IpPayloadSlice {
+                    ip_number,
+                    fragmented,
+                    len_source: LenSource::Ipv4HeaderTotalLen,
+                    payload: header_payload,
+                },
+            }),
+        }
+    }
+
+    /// Returns a slice containing the IPv4 header.
+    #[inline]
+    pub fn header(&self) -> Ipv4HeaderSlice {
+        self.header
+    }
+
+    /// Returns a slice containing the IPv4 extension headers.
+    #[inline]
+    pub fn extensions(&self) -> Ipv4ExtensionsSlice {
+        self.exts
+    }
+
+    /// Returns a slice containing the data after the IPv4 header
+    /// and IPv4 extensions headers.
+    #[inline]
+    pub fn payload(&self) -> &IpPayloadSlice<'a> {
+        &self.payload
+    }
+
+    /// Returns the ip number the type of payload of the IPv4 packet.
+    ///
+    /// This function returns the ip number stored in the last
+    /// IPv4 header or extension header.
+    #[inline]
+    pub fn payload_ip_number(&self) -> IpNumber {
+        self.payload.ip_number
+    }
+
+    /// Returns true if the payload is flagged as being fragmented.
+    #[inline]
+    pub fn is_payload_fragmented(&self) -> bool {
+        self.header.is_fragmenting_payload()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn debug_clone_eq(
+            ipv4_base in ipv4_any(),
+            auth in ip_auth_any()
+        ) {
+            let payload: [u8;4] = [1,2,3,4];
+            let mut data = Vec::with_capacity(
+                ipv4_base.header_len() +
+                auth.header_len() +
+                payload.len()
+            );
+            let mut ipv4 = ipv4_base.clone();
+            ipv4.protocol = crate::ip_number::AUTH;
+            ipv4.set_payload_len(auth.header_len() + payload.len()).unwrap();
+            data.extend_from_slice(&ipv4.to_bytes());
+            data.extend_from_slice(&auth.to_bytes());
+            data.extend_from_slice(&payload);
+
+            // decode packet
+            let slice = Ipv4Slice::from_slice(&data).unwrap();
+
+            // check debug output
+            prop_assert_eq!(
+                format!("{:?}", slice),
+                format!(
+                    "Ipv4Slice {{ header: {:?}, exts: {:?}, payload: {:?} }}",
+                    slice.header(),
+                    slice.extensions(),
+                    slice.payload()
+                )
+            );
+            prop_assert_eq!(slice.clone(), slice);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            ipv4_base in ipv4_any(),
+            auth in ip_auth_any()
+        ) {
+            let payload: [u8;6] = [1,2,3,4,5,6];
+
+            // build packets
+            let data_without_ext = {
+                let mut data = Vec::with_capacity(
+                    ipv4_base.header_len() +
+                    payload.len() +
+                    4
+                );
+                let mut ipv4 = ipv4_base.clone();
+                ipv4.set_payload_len(payload.len()).unwrap();
+                ipv4.protocol = crate::ip_number::UDP;
+                data.extend_from_slice(&ipv4.to_bytes());
+                data.extend_from_slice(&payload);
+                data.extend_from_slice(&[0,0,0,0]);
+                data
+            };
+            let data_with_ext = {
+                let payload: [u8;6] = [1,2,3,4,5,6];
+                let mut data = Vec::with_capacity(
+                    ipv4_base.header_len() +
+                    auth.header_len() +
+                    payload.len() +
+                    4
+                );
+                let mut ipv4 = ipv4_base.clone();
+                ipv4.set_payload_len(auth.header_len() + payload.len()).unwrap();
+                ipv4.protocol = crate::ip_number::AUTH;
+                data.extend_from_slice(&ipv4.to_bytes());
+                data.extend_from_slice(&auth.to_bytes());
+                data.extend_from_slice(&payload);
+                data.extend_from_slice(&[0,0,0,0]);
+                data
+            };
+
+            // parsing without extensions
+            {
+                let actual = Ipv4Slice::from_slice(&data_without_ext).unwrap();
+                prop_assert_eq!(actual.header().slice(), &data_without_ext[..ipv4_base.header_len()]);
+                prop_assert!(actual.extensions().auth.is_none());
+                prop_assert_eq!(
+                    &actual.payload,
+                    &IpPayloadSlice{
+                        ip_number: ip_number::UDP.into(),
+                        fragmented: ipv4_base.is_fragmenting_payload(),
+                        len_source: LenSource::Ipv4HeaderTotalLen,
+                        payload: &payload
+                    }
+                );
+                prop_assert_eq!(actual.payload_ip_number(), ip_number::UDP.into());
+            }
+
+            // parsing with extensions
+            {
+                let actual = Ipv4Slice::from_slice(&data_with_ext).unwrap();
+                prop_assert_eq!(actual.header().slice(), &data_with_ext[..ipv4_base.header_len()]);
+                prop_assert_eq!(
+                    actual.extensions().auth.unwrap(),
+                    IpAuthHeaderSlice::from_slice(&data_with_ext[ipv4_base.header_len()..]).unwrap()
+                );
+                prop_assert_eq!(
+                    &actual.payload,
+                    &IpPayloadSlice{
+                        ip_number: auth.next_header.into(),
+                        fragmented: ipv4_base.is_fragmenting_payload(),
+                        len_source: LenSource::Ipv4HeaderTotalLen,
+                        payload: &payload
+                    }
+                );
+                prop_assert_eq!(actual.payload_ip_number(), auth.next_header.into());
+            }
+
+            // header length error
+            for len in 0..Ipv4Header::MIN_LEN {
+                prop_assert_eq!(
+                    Ipv4Slice::from_slice(&data_without_ext[..len]).unwrap_err(),
+                    SliceError::Len(
+                        LenError{
+                            required_len: Ipv4Header::MIN_LEN,
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: Layer::Ipv4Header,
+                            layer_start_offset: 0,
+                        }
+                    )
+                );
+            }
+
+            // header content error
+            {
+                use crate::err::ipv4::HeaderError;
+                // inject invalid icv
+                let mut data = data_without_ext.clone();
+                data[0] = data[0] & 0xf0; // icv 0
+                prop_assert_eq!(
+                    Ipv4Slice::from_slice(&data).unwrap_err(),
+                    SliceError::Header(
+                        HeaderError::HeaderLengthSmallerThanHeader { ihl: 0 }
+                    )
+                );
+            }
+
+            // payload length error without auth header
+            {
+                use crate::err::{LenError, Layer};
+
+                let required_len = ipv4_base.header_len() + payload.len();
+                prop_assert_eq!(
+                    Ipv4Slice::from_slice(&data_without_ext[..required_len - 1]).unwrap_err(),
+                    SliceError::Len(LenError{
+                        required_len: required_len,
+                        len: required_len - 1,
+                        len_source: LenSource::Slice,
+                        layer: Layer::Ipv4Packet,
+                        layer_start_offset: 0,
+                    })
+                );
+            }
+
+            // payload length error auth header
+            {
+                use crate::err::{LenError, Layer};
+
+                let required_len = ipv4_base.header_len() + auth.header_len() + payload.len();
+                prop_assert_eq!(
+                    Ipv4Slice::from_slice(&data_with_ext[..required_len - 1]).unwrap_err(),
+                    SliceError::Len(LenError{
+                        required_len: required_len,
+                        len: required_len - 1,
+                        len_source: LenSource::Slice,
+                        layer: Layer::Ipv4Packet,
+                        layer_start_offset: 0,
+                    })
+                );
+            }
+
+            // auth length error
+            {
+                use crate::err::{LenError, Layer};
+
+                // inject a total_length that is smaller then the auth header
+                let mut data = data_with_ext.clone();
+                let total_len_too_small = ipv4_base.header_len() + auth.header_len() - 1;
+                {
+                    let tltsm = (total_len_too_small as u16).to_be_bytes();
+                    data[2] = tltsm[0];
+                    data[3] = tltsm[1];
+                }
+
+                prop_assert_eq!(
+                    Ipv4Slice::from_slice(&data).unwrap_err(),
+                    SliceError::Len(
+                        LenError{
+                            required_len: auth.header_len(),
+                            len: auth.header_len() - 1,
+                            len_source: LenSource::Ipv4HeaderTotalLen,
+                            layer: Layer::IpAuthHeader,
+                            layer_start_offset: ipv4_base.header_len(),
+                        }
+                    )
+                );
+            }
+
+            // auth content error
+            {
+                use crate::err::ip_auth::HeaderError;
+
+                // inject zero as auth header length
+                let mut data = data_with_ext.clone();
+                data[ipv4_base.header_len() + 1] = 0;
+
+                prop_assert_eq!(
+                    Ipv4Slice::from_slice(&data).unwrap_err(),
+                    SliceError::Exts(
+                        HeaderError::ZeroPayloadLen
+                    )
+                );
+            }
+        }
+    }
+
+    #[test]
+    fn is_payload_fragmented() {
+        use crate::ip_number::UDP;
+        // non-fragmented
+        {
+            let payload: [u8; 6] = [1, 2, 3, 4, 5, 6];
+            let ipv4 =
+                Ipv4Header::new(payload.len() as u16, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10]).unwrap();
+            let data = {
+                let mut data = Vec::with_capacity(ipv4.header_len() + payload.len());
+                data.extend_from_slice(&ipv4.to_bytes());
+                data.extend_from_slice(&payload);
+                data
+            };
+
+            let slice = Ipv4Slice::from_slice(&data).unwrap();
+            assert!(false == slice.is_payload_fragmented());
+        }
+        // fragmented
+        {
+            let payload: [u8; 6] = [1, 2, 3, 4, 5, 6];
+            let mut ipv4 =
+                Ipv4Header::new(payload.len() as u16, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10]).unwrap();
+            ipv4.fragment_offset = 123.try_into().unwrap();
+            let data = {
+                let mut data = Vec::with_capacity(ipv4.header_len() + payload.len());
+                data.extend_from_slice(&ipv4.to_bytes());
+                data.extend_from_slice(&payload);
+                data
+            };
+
+            let slice = Ipv4Slice::from_slice(&data).unwrap();
+            assert!(slice.is_payload_fragmented());
+        }
+    }
+}
diff --git a/src/net/ipv6_ext_slice.rs b/src/net/ipv6_ext_slice.rs
new file mode 100644
index 0000000..ff1a749
--- /dev/null
+++ b/src/net/ipv6_ext_slice.rs
@@ -0,0 +1,99 @@
+use crate::*;
+
+/// Enum containing a slice of a supported ipv6 extension header.
+///
+/// This enum is used as item type when iterating over a list of extension headers
+/// with an [Ipv6ExtensionSliceIter].
+///
+/// Note the following extension headers are missing from
+/// this enum and currently not supported (list taken on 2021-07-17
+/// from <https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml>):
+///
+/// * Encapsulating Security Payload \[[RFC4303](https://datatracker.ietf.org/doc/html/rfc4303)\]
+/// * Mobility Header \[[RFC6275](https://datatracker.ietf.org/doc/html/rfc6275)\]
+/// * Host Identity Protocol \[[RFC7401](https://datatracker.ietf.org/doc/html/rfc7401)\]
+/// * Shim6 Protocol \[[RFC5533](https://datatracker.ietf.org/doc/html/rfc5533)\]
+/// * 253 Use for experimentation and testing \[[RFC3692](https://datatracker.ietf.org/doc/html/rfc3692)\]\[[RFC4727](https://datatracker.ietf.org/doc/html/rfc4727)\]
+/// * 254 Use for experimentation and testing \[[RFC3692](https://datatracker.ietf.org/doc/html/rfc3692)\]\[[RFC4727](https://datatracker.ietf.org/doc/html/rfc4727)\]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum Ipv6ExtensionSlice<'a> {
+    /// IPv6 Hop-by-Hop Option \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\]
+    HopByHop(Ipv6RawExtHeaderSlice<'a>),
+    /// Routing Header for IPv6 \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\] \[[RFC5095](https://datatracker.ietf.org/doc/html/rfc5095)\]
+    Routing(Ipv6RawExtHeaderSlice<'a>),
+    /// Fragment Header for IPv6 \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\]
+    Fragment(Ipv6FragmentHeaderSlice<'a>),
+    /// Destination Options for IPv6 \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\]
+    DestinationOptions(Ipv6RawExtHeaderSlice<'a>),
+    /// Authentication Header \[[RFC4302](https://datatracker.ietf.org/doc/html/rfc4302)\]
+    Authentication(IpAuthHeaderSlice<'a>),
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::ip_number::*;
+
+    #[test]
+    fn debug() {
+        use alloc::{format, vec::Vec};
+        use Ipv6ExtensionSlice::*;
+        {
+            let header = Ipv6RawExtHeader::new_raw(UDP, &[1, 2, 3, 4, 5, 6]).unwrap();
+            let mut buffer = Vec::with_capacity(header.header_len());
+            header.write(&mut buffer).unwrap();
+            let slice = Ipv6RawExtHeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(
+                format!("HopByHop({:?})", slice),
+                format!("{:?}", HopByHop(slice.clone()))
+            );
+            assert_eq!(
+                format!("Routing({:?})", slice),
+                format!("{:?}", Routing(slice.clone()))
+            );
+            assert_eq!(
+                format!("DestinationOptions({:?})", slice),
+                format!("{:?}", DestinationOptions(slice.clone()))
+            );
+        }
+        {
+            let header = Ipv6FragmentHeader::new(UDP, 1.try_into().unwrap(), true, 2);
+            let mut buffer = Vec::with_capacity(header.header_len());
+            header.write(&mut buffer).unwrap();
+            let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(
+                format!("Fragment({:?})", slice),
+                format!("{:?}", Fragment(slice))
+            );
+        }
+        {
+            let header = IpAuthHeader::new(UDP, 1, 2, &[1, 2, 3, 4]).unwrap();
+            let mut buffer = Vec::with_capacity(header.header_len());
+            header.write(&mut buffer).unwrap();
+            let slice = IpAuthHeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(
+                format!("Authentication({:?})", slice),
+                format!("{:?}", Authentication(slice.clone()))
+            );
+        }
+    }
+
+    #[test]
+    fn clone_eq() {
+        use alloc::vec::Vec;
+        use Ipv6ExtensionSlice::*;
+
+        let header = Ipv6RawExtHeader::new_raw(UDP, &[1, 2, 3, 4, 5, 6]).unwrap();
+        let mut buffer = Vec::with_capacity(header.header_len());
+        header.write(&mut buffer).unwrap();
+        let slice = Ipv6RawExtHeaderSlice::from_slice(&buffer).unwrap();
+
+        let hop = HopByHop(slice.clone());
+        assert_eq!(hop.clone(), hop.clone());
+
+        let route = Routing(slice.clone());
+        assert_eq!(route.clone(), route.clone());
+
+        assert_ne!(route, hop);
+    }
+}
diff --git a/src/net/ipv6_ext_slice_iter.rs b/src/net/ipv6_ext_slice_iter.rs
new file mode 100644
index 0000000..cbf720d
--- /dev/null
+++ b/src/net/ipv6_ext_slice_iter.rs
@@ -0,0 +1,212 @@
+use crate::*;
+use core::slice::from_raw_parts;
+
+/// Allows iterating over the IPv6 extension headers present in an [Ipv6ExtensionsSlice].
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Ipv6ExtensionSliceIter<'a> {
+    pub(crate) next_header: IpNumber,
+    pub(crate) rest: &'a [u8],
+}
+
+impl<'a> Default for Ipv6ExtensionSliceIter<'a> {
+    fn default() -> Self {
+        Ipv6ExtensionSliceIter {
+            // don't use 0 as this is the reserved value
+            // for the hop by hop header
+            next_header: IpNumber::IPV6_NO_NEXT_HEADER,
+            rest: &[],
+        }
+    }
+}
+
+impl<'a> Iterator for Ipv6ExtensionSliceIter<'a> {
+    type Item = Ipv6ExtensionSlice<'a>;
+
+    fn next(&mut self) -> Option<Ipv6ExtensionSlice<'a>> {
+        use ip_number::*;
+        use Ipv6ExtensionSlice::*;
+
+        match self.next_header {
+            // Note on the unsafe calls:
+            //
+            // As the slice contents & length were previously checked by
+            // Ipv6ExtensionsSlice::from_slice the content does not have to be
+            // rechecked.
+            IPV6_HOP_BY_HOP => unsafe {
+                let slice = Ipv6RawExtHeaderSlice::from_slice_unchecked(self.rest);
+                let len = slice.slice().len();
+                self.rest = from_raw_parts(self.rest.as_ptr().add(len), self.rest.len() - len);
+                self.next_header = slice.next_header();
+                Some(HopByHop(slice))
+            },
+            IPV6_ROUTE => unsafe {
+                let slice = Ipv6RawExtHeaderSlice::from_slice_unchecked(self.rest);
+                let len = slice.slice().len();
+                self.rest = from_raw_parts(self.rest.as_ptr().add(len), self.rest.len() - len);
+                self.next_header = slice.next_header();
+                Some(Routing(slice))
+            },
+            IPV6_DEST_OPTIONS => unsafe {
+                let slice = Ipv6RawExtHeaderSlice::from_slice_unchecked(self.rest);
+                let len = slice.slice().len();
+                self.rest = from_raw_parts(self.rest.as_ptr().add(len), self.rest.len() - len);
+                self.next_header = slice.next_header();
+                Some(DestinationOptions(slice))
+            },
+            IPV6_FRAG => unsafe {
+                let slice = Ipv6FragmentHeaderSlice::from_slice_unchecked(self.rest);
+                let len = slice.slice().len();
+                self.rest = from_raw_parts(self.rest.as_ptr().add(len), self.rest.len() - len);
+                self.next_header = slice.next_header();
+
+                Some(Fragment(slice))
+            },
+            AUTH => unsafe {
+                let slice = IpAuthHeaderSlice::from_slice_unchecked(self.rest);
+                let len = slice.slice().len();
+                self.rest = from_raw_parts(self.rest.as_ptr().add(len), self.rest.len() - len);
+                self.next_header = slice.next_header();
+                Some(Authentication(slice))
+            },
+            // done parsing, the next header is not a known/supported header extension
+            _ => None,
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::ipv6_exts_test_helpers::*;
+    use super::*;
+    use crate::ip_number::*;
+    use crate::test_gens::*;
+    use alloc::borrow::ToOwned;
+    use proptest::prelude::*;
+
+    #[test]
+    fn into_iter() {
+        let a: Ipv6ExtensionsSlice = Default::default();
+        let mut iter = a.into_iter();
+        assert_eq!(None, iter.next());
+    }
+
+    proptest! {
+        #[test]
+        fn next(
+            header_size in any::<u8>(),
+            post_header in ip_number_any()
+                .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(),
+                    |v| !EXTENSION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x)
+                )
+        ) {
+            /// Run a test with the given ip numbers
+            fn run_test(ip_numbers: &[IpNumber], header_sizes: &[u8]) {
+                // setup test payload
+                let e = ExtensionTestPayload::new(
+                    ip_numbers,
+                    header_sizes
+                );
+
+                // a hop by hop header that is not at the start triggers an error
+                if false == e.ip_numbers[1..].iter().any(|&x| x == IPV6_HOP_BY_HOP) {
+                    // normal read
+                    let (header, _, _) = Ipv6ExtensionsSlice::from_slice(ip_numbers[0], e.slice()).unwrap();
+                    let mut iter = header.into_iter();
+                    let mut slice = e.slice();
+
+                    // go through all expected headers
+                    for i in 0..e.ip_numbers.len() - 1 {
+                        use Ipv6ExtensionSlice::*;
+
+                        // iterate and check all results
+                        let next = iter.next().unwrap();
+                        match e.ip_numbers[i] {
+                            IPV6_HOP_BY_HOP => {
+                                let header = Ipv6RawExtHeaderSlice::from_slice(slice).unwrap();
+                                assert_eq!(next, HopByHop(header.clone()));
+                                slice = &slice[header.slice().len()..];
+                            },
+                            IPV6_ROUTE => {
+                                let header = Ipv6RawExtHeaderSlice::from_slice(slice).unwrap();
+                                assert_eq!(next, Routing(header.clone()));
+                                slice = &slice[header.slice().len()..];
+                            },
+                            IPV6_DEST_OPTIONS => {
+                                let header = Ipv6RawExtHeaderSlice::from_slice(slice).unwrap();
+                                assert_eq!(next, DestinationOptions(header.clone()));
+                                slice = &slice[header.slice().len()..];
+                            }
+                            IPV6_FRAG => {
+                                let header = Ipv6FragmentHeaderSlice::from_slice(slice).unwrap();
+                                assert_eq!(next, Fragment(header.clone()));
+                                slice = &slice[header.slice().len()..];
+                            },
+                            AUTH => {
+                                let header = IpAuthHeaderSlice::from_slice(slice).unwrap();
+                                assert_eq!(next, Authentication(header.clone()));
+                                slice = &slice[header.slice().len()..];
+                            },
+                            _ => unreachable!()
+                        }
+                    }
+
+                    // expect that all headers have been visited
+                    assert_eq!(None, iter.next());
+                }
+            }
+
+            // test the parsing of different extension header combinations
+            for first_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                // single header parsing
+                run_test(
+                    &[*first_header, post_header],
+                    &[header_size],
+                );
+
+                for second_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                    // double header parsing
+                    run_test(
+                        &[*first_header, *second_header, post_header],
+                        &[header_size],
+                    );
+
+                    for third_header in &EXTENSION_KNOWN_IP_NUMBERS {
+                        // tripple header parsing
+                        run_test(
+                            &[*first_header, *second_header, *third_header, post_header],
+                            &[header_size],
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn debug() {
+        use alloc::format;
+
+        let a: Ipv6ExtensionSliceIter = Default::default();
+        assert_eq!(
+            format!(
+                "Ipv6ExtensionSliceIter {{ next_header: {:?}, rest: [] }}",
+                IpNumber(59)
+            ),
+            format!("{:?}", a)
+        );
+    }
+
+    #[test]
+    fn clone_eq() {
+        let a: Ipv6ExtensionSliceIter = Default::default();
+        assert_eq!(a.clone(), a);
+    }
+
+    #[test]
+    fn default() {
+        let mut a: Ipv6ExtensionSliceIter = Default::default();
+        assert_eq!(None, a.next());
+    }
+}
diff --git a/src/net/ipv6_exts.rs b/src/net/ipv6_exts.rs
new file mode 100644
index 0000000..afbd104
--- /dev/null
+++ b/src/net/ipv6_exts.rs
@@ -0,0 +1,2638 @@
+use crate::{
+    err::{ipv6_exts::*, Layer},
+    *,
+};
+
+/// IPv6 extension headers present after the ip header.
+///
+/// Currently supported:
+///
+/// * Authentication Header
+/// * Hop by Hop Options Header
+/// * Destination Options Header (before and after routing headers)
+/// * Routing Header
+/// * Fragment
+/// * Authentication Header
+///
+/// Currently not supported:
+///
+/// * Encapsulating Security Payload Header (ESP)
+/// * Host Identity Protocol (HIP)
+/// * IP Mobility
+/// * Site Multihoming by IPv6 Intermediation (SHIM6)
+#[derive(Clone, Debug, Eq, PartialEq, Default)]
+pub struct Ipv6Extensions {
+    pub hop_by_hop_options: Option<Ipv6RawExtHeader>,
+    pub destination_options: Option<Ipv6RawExtHeader>,
+    pub routing: Option<Ipv6RoutingExtensions>,
+    pub fragment: Option<Ipv6FragmentHeader>,
+    pub auth: Option<IpAuthHeader>,
+}
+
+impl Ipv6Extensions {
+    /// Minimum length required for extension header in bytes/octets.
+    /// Which is zero as no extension headers are required.
+    pub const MIN_LEN: usize = 0;
+
+    /// Maximum summed up length of all extension headers in bytes/octets.
+    pub const MAX_LEN: usize = Ipv6RawExtHeader::MAX_LEN * 2
+        + Ipv6RoutingExtensions::MAX_LEN
+        + Ipv6FragmentHeader::LEN
+        + IpAuthHeader::MAX_LEN;
+
+    /// Reads as many extension headers as possible from the slice.
+    ///
+    /// Returns the found ipv6 extension headers, the next header ip number after the read
+    /// headers and a slice containing the rest of the packet after the read headers.
+    ///
+    /// Note that this function can only handle ipv6 extensions if each extension header does
+    /// occur at most once, except for destination options headers which are allowed to
+    /// exist once in front of a routing header and once after a routing header.
+    ///
+    /// In case that more extension headers then can fit into a `Ipv6Extensions` struct are
+    /// encountered, the parsing is stoped at the point where the data would no longer fit into
+    /// the struct. In such a scenario a struct with the data that could be parsed is returned
+    /// together with the next header ip number and slice containing the unparsed data.
+    ///
+    /// It is in the responsibility of the caller to handle a scenario like this.
+    ///
+    /// The reason that no error is generated, is that even though according to RFC 8200 packets
+    /// "should" not contain more then one occurence of an extension header the RFC also specifies
+    /// that "IPv6 nodes must accept and attempt to process extension headers in any order and
+    /// occurring any number of times in the same packet". So packets with multiple headers "should"
+    /// not exist, but are still valid IPv6 packets. As such this function does not generate a
+    /// parsing error, as it is not an invalid packet, but if packets like these are encountered
+    /// the user of this function has to themself decide how to handle packets like these.
+    ///
+    /// The only exception is if an hop by hop header is located somewhere else then directly at
+    /// the start. In this case an `ReadError::Ipv6HopByHopHeaderNotAtStart` error is generated as
+    /// the hop by hop header is required to be located directly after the IPv6 header according
+    /// to RFC 8200.
+    pub fn from_slice(
+        start_ip_number: IpNumber,
+        slice: &[u8],
+    ) -> Result<(Ipv6Extensions, IpNumber, &[u8]), err::ipv6_exts::HeaderSliceError> {
+        let mut result: Ipv6Extensions = Default::default();
+        let mut rest = slice;
+        let mut next_header = start_ip_number;
+
+        use err::ipv6_exts::{HeaderError::*, HeaderSliceError::*};
+        use ip_number::*;
+
+        // the hop by hop header is required to occur directly after the ipv6 header
+        if IPV6_HOP_BY_HOP == next_header {
+            let slice = Ipv6RawExtHeaderSlice::from_slice(rest).map_err(Len)?;
+            rest = &rest[slice.slice().len()..];
+            next_header = slice.next_header();
+            result.hop_by_hop_options = Some(slice.to_header());
+        }
+
+        loop {
+            match next_header {
+                IPV6_HOP_BY_HOP => {
+                    return Err(Content(HopByHopNotAtStart));
+                }
+                IPV6_DEST_OPTIONS => {
+                    if let Some(ref mut routing) = result.routing {
+                        // if the routing header is already present
+                        // this this a "final destination options" header
+                        if routing.final_destination_options.is_some() {
+                            // more then one header of this type found -> abort parsing
+                            return Ok((result, next_header, rest));
+                        } else {
+                            let slice = Ipv6RawExtHeaderSlice::from_slice(rest)
+                                .map_err(|err| Len(err.add_offset(slice.len() - rest.len())))?;
+                            rest = &rest[slice.slice().len()..];
+                            next_header = slice.next_header();
+                            routing.final_destination_options = Some(slice.to_header());
+                        }
+                    } else if result.destination_options.is_some() {
+                        // more then one header of this type found -> abort parsing
+                        return Ok((result, next_header, rest));
+                    } else {
+                        let slice = Ipv6RawExtHeaderSlice::from_slice(rest)
+                            .map_err(|err| Len(err.add_offset(slice.len() - rest.len())))?;
+                        rest = &rest[slice.slice().len()..];
+                        next_header = slice.next_header();
+                        result.destination_options = Some(slice.to_header());
+                    }
+                }
+                IPV6_ROUTE => {
+                    if result.routing.is_some() {
+                        // more then one header of this type found -> abort parsing
+                        return Ok((result, next_header, rest));
+                    } else {
+                        let slice = Ipv6RawExtHeaderSlice::from_slice(rest)
+                            .map_err(|err| Len(err.add_offset(slice.len() - rest.len())))?;
+                        rest = &rest[slice.slice().len()..];
+                        next_header = slice.next_header();
+                        result.routing = Some(Ipv6RoutingExtensions {
+                            routing: slice.to_header(),
+                            final_destination_options: None,
+                        });
+                    }
+                }
+                IPV6_FRAG => {
+                    if result.fragment.is_some() {
+                        // more then one header of this type found -> abort parsing
+                        return Ok((result, next_header, rest));
+                    } else {
+                        let slice = Ipv6FragmentHeaderSlice::from_slice(rest)
+                            .map_err(|err| Len(err.add_offset(slice.len() - rest.len())))?;
+                        rest = &rest[slice.slice().len()..];
+                        next_header = slice.next_header();
+                        result.fragment = Some(slice.to_header());
+                    }
+                }
+                AUTH => {
+                    if result.auth.is_some() {
+                        // more then one header of this type found -> abort parsing
+                        return Ok((result, next_header, rest));
+                    } else {
+                        let slice = IpAuthHeaderSlice::from_slice(rest).map_err(|err| {
+                            use err::ip_auth::HeaderSliceError as I;
+                            use err::ipv6_exts::HeaderError as O;
+                            match err {
+                                I::Len(err) => Len(err.add_offset(slice.len() - rest.len())),
+                                I::Content(err) => Content(O::IpAuth(err)),
+                            }
+                        })?;
+                        rest = &rest[slice.slice().len()..];
+                        next_header = slice.next_header();
+                        result.auth = Some(slice.to_header());
+                    }
+                }
+                _ => {
+                    // done parsing, the next header is not a known header extension
+                    return Ok((result, next_header, rest));
+                }
+            }
+        }
+        //should not be hit
+    }
+
+    /// Reads as many extension headers as possible from the slice until a non IPv6 extension
+    /// header or an error gets encountered.
+    ///
+    /// This function differs from [`Ipv6Extensions::from_slice`] in that it returns the successfully
+    /// parsed parts together with the error. While [`Ipv6Extensions::from_slice`] only returns an
+    /// error.
+    ///
+    /// Note that this function (same as [`Ipv6Extensions::from_slice`]) will stop parsing as soon
+    /// as more headers then can be stored in [`Ipv6Extensions`] are encountered. E.g. if there is
+    /// more then one "auth" header the function returns as soon as the second "auth" header is
+    /// encountered.
+    pub fn from_slice_lax(
+        start_ip_number: IpNumber,
+        slice: &[u8],
+    ) -> (
+        Ipv6Extensions,
+        IpNumber,
+        &[u8],
+        Option<(err::ipv6_exts::HeaderSliceError, err::Layer)>,
+    ) {
+        let mut result: Ipv6Extensions = Default::default();
+        let mut rest = slice;
+        let mut next_header = start_ip_number;
+
+        use err::ipv6_exts::{HeaderError::*, HeaderSliceError::*};
+        use ip_number::*;
+
+        // the hop by hop header is required to occur directly after the ipv6 header
+        if IPV6_HOP_BY_HOP == next_header {
+            match Ipv6RawExtHeaderSlice::from_slice(rest) {
+                Ok(slice) => {
+                    rest = &rest[slice.slice().len()..];
+                    next_header = slice.next_header();
+                    result.hop_by_hop_options = Some(slice.to_header());
+                }
+                Err(error) => {
+                    return (
+                        result,
+                        next_header,
+                        rest,
+                        Some((Len(error), Layer::Ipv6HopByHopHeader)),
+                    );
+                }
+            }
+        }
+
+        loop {
+            match next_header {
+                IPV6_HOP_BY_HOP => {
+                    return (
+                        result,
+                        next_header,
+                        rest,
+                        Some((Content(HopByHopNotAtStart), Layer::Ipv6HopByHopHeader)),
+                    );
+                }
+                IPV6_DEST_OPTIONS => {
+                    if let Some(ref mut routing) = result.routing {
+                        // if the routing header is already present
+                        // this this a "final destination options" header
+                        if routing.final_destination_options.is_some() {
+                            // more then one header of this type found -> abort parsing
+                            return (result, next_header, rest, None);
+                        } else {
+                            match Ipv6RawExtHeaderSlice::from_slice(rest) {
+                                Ok(slice) => {
+                                    rest = &rest[slice.slice().len()..];
+                                    next_header = slice.next_header();
+                                    routing.final_destination_options = Some(slice.to_header());
+                                }
+                                Err(err) => {
+                                    return (
+                                        result,
+                                        next_header,
+                                        rest,
+                                        Some((
+                                            Len(err.add_offset(slice.len() - rest.len())),
+                                            Layer::Ipv6DestOptionsHeader,
+                                        )),
+                                    );
+                                }
+                            }
+                        }
+                    } else if result.destination_options.is_some() {
+                        // more then one header of this type found -> abort parsing
+                        return (result, next_header, rest, None);
+                    } else {
+                        match Ipv6RawExtHeaderSlice::from_slice(rest) {
+                            Ok(slice) => {
+                                rest = &rest[slice.slice().len()..];
+                                next_header = slice.next_header();
+                                result.destination_options = Some(slice.to_header());
+                            }
+                            Err(err) => {
+                                return (
+                                    result,
+                                    next_header,
+                                    rest,
+                                    Some((
+                                        Len(err.add_offset(slice.len() - rest.len())),
+                                        Layer::Ipv6DestOptionsHeader,
+                                    )),
+                                );
+                            }
+                        }
+                    }
+                }
+                IPV6_ROUTE => {
+                    if result.routing.is_some() {
+                        // more then one header of this type found -> abort parsing
+                        return (result, next_header, rest, None);
+                    } else {
+                        match Ipv6RawExtHeaderSlice::from_slice(rest) {
+                            Ok(slice) => {
+                                rest = &rest[slice.slice().len()..];
+                                next_header = slice.next_header();
+                                result.routing = Some(Ipv6RoutingExtensions {
+                                    routing: slice.to_header(),
+                                    final_destination_options: None,
+                                });
+                            }
+                            Err(err) => {
+                                return (
+                                    result,
+                                    next_header,
+                                    rest,
+                                    Some((
+                                        Len(err.add_offset(slice.len() - rest.len())),
+                                        Layer::Ipv6RouteHeader,
+                                    )),
+                                );
+                            }
+                        }
+                    }
+                }
+                IPV6_FRAG => {
+                    if result.fragment.is_some() {
+                        // more then one header of this type found -> abort parsing
+                        return (result, next_header, rest, None);
+                    } else {
+                        match Ipv6FragmentHeaderSlice::from_slice(rest) {
+                            Ok(slice) => {
+                                rest = &rest[slice.slice().len()..];
+                                next_header = slice.next_header();
+                                result.fragment = Some(slice.to_header());
+                            }
+                            Err(err) => {
+                                return (
+                                    result,
+                                    next_header,
+                                    rest,
+                                    Some((
+                                        Len(err.add_offset(slice.len() - rest.len())),
+                                        Layer::Ipv6FragHeader,
+                                    )),
+                                );
+                            }
+                        }
+                    }
+                }
+                AUTH => {
+                    if result.auth.is_some() {
+                        // more then one header of this type found -> abort parsing
+                        return (result, next_header, rest, None);
+                    } else {
+                        match IpAuthHeaderSlice::from_slice(rest) {
+                            Ok(slice) => {
+                                rest = &rest[slice.slice().len()..];
+                                next_header = slice.next_header();
+                                result.auth = Some(slice.to_header());
+                            }
+                            Err(err) => {
+                                use err::ip_auth::HeaderSliceError as I;
+                                use err::ipv6_exts::HeaderError as O;
+                                return (
+                                    result,
+                                    next_header,
+                                    rest,
+                                    Some((
+                                        match err {
+                                            I::Len(err) => {
+                                                Len(err.add_offset(slice.len() - rest.len()))
+                                            }
+                                            I::Content(err) => Content(O::IpAuth(err)),
+                                        },
+                                        Layer::IpAuthHeader,
+                                    )),
+                                );
+                            }
+                        }
+                    }
+                }
+                _ => {
+                    // done parsing, the next header is not a known header extension
+                    return (result, next_header, rest, None);
+                }
+            }
+        }
+        //should not be hit
+    }
+
+    /// Reads as many extension headers as possible from the reader and returns the found ipv6
+    /// extension headers and the next header ip number.
+    ///
+    /// If no extension headers are present an unfilled struct and the original `first_header`
+    /// ip number is returned.
+    ///
+    /// Note that this function can only handle ipv6 extensions if each extension header does
+    /// occur at most once, except for destination options headers which are allowed to
+    /// exist once in front of a routing header and once after a routing header.
+    ///
+    /// In case that more extension headers then can fit into a `Ipv6Extensions` struct are
+    /// encountered, the parsing is stoped at the point where the data would no longer fit into
+    /// the struct. In such a scenario a struct with the data that could be parsed is returned
+    /// together with the next header ip number that identfies which header could be read next.
+    ///
+    /// It is in the responsibility of the caller to handle a scenario like this.
+    ///
+    /// The reason that no error is generated, is that even though according to RFC 8200, packets
+    /// "should" not contain more then one occurence of an extension header, the RFC also specifies
+    /// that "IPv6 nodes must accept and attempt to process extension headers in any order and
+    /// occurring any number of times in the same packet". So packets with multiple headers "should"
+    /// not exist, but are still valid IPv6 packets. As such this function does not generate a
+    /// parsing error, as it is not an invalid packet, but if packets like these are encountered
+    /// the user of this function has to themself decide how to handle packets like these.
+    ///
+    /// The only exception is if an hop by hop header is located somewhere else then directly at
+    /// the start. In this case an `ReadError::Ipv6HopByHopHeaderNotAtStart` error is generated as
+    /// the hop by hop header is required to be located directly after the IPv6 header according
+    /// to RFC 8200.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut T,
+        start_ip_number: IpNumber,
+    ) -> Result<(Ipv6Extensions, IpNumber), err::ipv6_exts::HeaderReadError> {
+        let mut result: Ipv6Extensions = Default::default();
+        let mut next_protocol = start_ip_number;
+
+        use err::ipv6_exts::{HeaderError::*, HeaderReadError::*};
+        use ip_number::*;
+
+        // the hop by hop header is required to occur directly after the ipv6 header
+        if IPV6_HOP_BY_HOP == next_protocol {
+            let header = Ipv6RawExtHeader::read(reader).map_err(Io)?;
+            next_protocol = header.next_header;
+            result.hop_by_hop_options = Some(header);
+        }
+
+        loop {
+            match next_protocol {
+                IPV6_HOP_BY_HOP => {
+                    return Err(Content(HopByHopNotAtStart));
+                }
+                IPV6_DEST_OPTIONS => {
+                    if let Some(ref mut routing) = result.routing {
+                        // if the routing header is already present
+                        // asume this is a "final destination options" header
+                        if routing.final_destination_options.is_some() {
+                            // more then one header of this type found -> abort parsing
+                            return Ok((result, next_protocol));
+                        } else {
+                            let header = Ipv6RawExtHeader::read(reader).map_err(Io)?;
+                            next_protocol = header.next_header;
+                            routing.final_destination_options = Some(header);
+                        }
+                    } else if result.destination_options.is_some() {
+                        // more then one header of this type found -> abort parsing
+                        return Ok((result, next_protocol));
+                    } else {
+                        let header = Ipv6RawExtHeader::read(reader).map_err(Io)?;
+                        next_protocol = header.next_header;
+                        result.destination_options = Some(header);
+                    }
+                }
+                IPV6_ROUTE => {
+                    if result.routing.is_some() {
+                        // more then one header of this type found -> abort parsing
+                        return Ok((result, next_protocol));
+                    } else {
+                        let header = Ipv6RawExtHeader::read(reader).map_err(Io)?;
+                        next_protocol = header.next_header;
+                        result.routing = Some(Ipv6RoutingExtensions {
+                            routing: header,
+                            final_destination_options: None,
+                        });
+                    }
+                }
+                IPV6_FRAG => {
+                    if result.fragment.is_some() {
+                        // more then one header of this type found -> abort parsing
+                        return Ok((result, next_protocol));
+                    } else {
+                        let header = Ipv6FragmentHeader::read(reader).map_err(Io)?;
+                        next_protocol = header.next_header;
+                        result.fragment = Some(header);
+                    }
+                }
+                AUTH => {
+                    if result.auth.is_some() {
+                        // more then one header of this type found -> abort parsing
+                        return Ok((result, next_protocol));
+                    } else {
+                        let header = IpAuthHeader::read(reader).map_err(|err| {
+                            use err::ip_auth::HeaderReadError as I;
+                            match err {
+                                I::Io(err) => Io(err),
+                                I::Content(err) => Content(IpAuth(err)),
+                            }
+                        })?;
+                        next_protocol = header.next_header;
+                        result.auth = Some(header);
+                    }
+                }
+                _ => {
+                    // done parsing, the next header is not a known header extension
+                    return Ok((result, next_protocol));
+                }
+            }
+        }
+
+        //should not be hit
+    }
+
+    /// Reads as many extension headers as possible from the limited reader and returns the found ipv6
+    /// extension headers and the next header ip number.
+    ///
+    /// If no extension headers are present an unfilled struct and the original `first_header`
+    /// ip number is returned.
+    ///
+    /// Note that this function can only handle ipv6 extensions if each extension header does
+    /// occur at most once, except for destination options headers which are allowed to
+    /// exist once in front of a routing header and once after a routing header.
+    ///
+    /// In case that more extension headers then can fit into a `Ipv6Extensions` struct are
+    /// encountered, the parsing is stoped at the point where the data would no longer fit into
+    /// the struct. In such a scenario a struct with the data that could be parsed is returned
+    /// together with the next header ip number that identfies which header could be read next.
+    ///
+    /// It is in the responsibility of the caller to handle a scenario like this.
+    ///
+    /// The reason that no error is generated, is that even though according to RFC 8200, packets
+    /// "should" not contain more then one occurence of an extension header, the RFC also specifies
+    /// that "IPv6 nodes must accept and attempt to process extension headers in any order and
+    /// occurring any number of times in the same packet". So packets with multiple headers "should"
+    /// not exist, but are still valid IPv6 packets. As such this function does not generate a
+    /// parsing error, as it is not an invalid packet, but if packets like these are encountered
+    /// the user of this function has to themself decide how to handle packets like these.
+    ///
+    /// The only exception is if an hop by hop header is located somewhere else then directly at
+    /// the start. In this case an `ReadError::Ipv6HopByHopHeaderNotAtStart` error is generated as
+    /// the hop by hop header is required to be located directly after the IPv6 header according
+    /// to RFC 8200.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read_limited<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut crate::io::LimitedReader<T>,
+        start_ip_number: IpNumber,
+    ) -> Result<(Ipv6Extensions, IpNumber), HeaderLimitedReadError> {
+        use ip_number::*;
+        use HeaderError::*;
+        use HeaderLimitedReadError::*;
+
+        fn map_limited_err(err: err::io::LimitedReadError) -> HeaderLimitedReadError {
+            use crate::err::io::LimitedReadError as I;
+            match err {
+                I::Io(err) => Io(err),
+                I::Len(err) => Len(err),
+            }
+        }
+
+        // start decoding
+        let mut result: Ipv6Extensions = Default::default();
+        let mut next_protocol = start_ip_number;
+
+        // the hop by hop header is required to occur directly after the ipv6 header
+        if IPV6_HOP_BY_HOP == next_protocol {
+            let header = Ipv6RawExtHeader::read_limited(reader).map_err(map_limited_err)?;
+            next_protocol = header.next_header;
+            result.hop_by_hop_options = Some(header);
+        }
+
+        loop {
+            match next_protocol {
+                IPV6_HOP_BY_HOP => {
+                    return Err(Content(HopByHopNotAtStart));
+                }
+                IPV6_DEST_OPTIONS => {
+                    if let Some(ref mut routing) = result.routing {
+                        // if the routing header is already present
+                        // asume this is a "final destination options" header
+                        if routing.final_destination_options.is_some() {
+                            // more then one header of this type found -> abort parsing
+                            return Ok((result, next_protocol));
+                        } else {
+                            let header =
+                                Ipv6RawExtHeader::read_limited(reader).map_err(map_limited_err)?;
+                            next_protocol = header.next_header;
+                            routing.final_destination_options = Some(header);
+                        }
+                    } else if result.destination_options.is_some() {
+                        // more then one header of this type found -> abort parsing
+                        return Ok((result, next_protocol));
+                    } else {
+                        let header =
+                            Ipv6RawExtHeader::read_limited(reader).map_err(map_limited_err)?;
+                        next_protocol = header.next_header;
+                        result.destination_options = Some(header);
+                    }
+                }
+                IPV6_ROUTE => {
+                    if result.routing.is_some() {
+                        // more then one header of this type found -> abort parsing
+                        return Ok((result, next_protocol));
+                    } else {
+                        let header =
+                            Ipv6RawExtHeader::read_limited(reader).map_err(map_limited_err)?;
+                        next_protocol = header.next_header;
+                        result.routing = Some(Ipv6RoutingExtensions {
+                            routing: header,
+                            final_destination_options: None,
+                        });
+                    }
+                }
+                IPV6_FRAG => {
+                    if result.fragment.is_some() {
+                        // more then one header of this type found -> abort parsing
+                        return Ok((result, next_protocol));
+                    } else {
+                        let header =
+                            Ipv6FragmentHeader::read_limited(reader).map_err(map_limited_err)?;
+                        next_protocol = header.next_header;
+                        result.fragment = Some(header);
+                    }
+                }
+                AUTH => {
+                    if result.auth.is_some() {
+                        // more then one header of this type found -> abort parsing
+                        return Ok((result, next_protocol));
+                    } else {
+                        let header = IpAuthHeader::read_limited(reader).map_err(|err| {
+                            use err::ip_auth::HeaderLimitedReadError as I;
+                            match err {
+                                I::Io(err) => Io(err),
+                                I::Len(err) => Len(err),
+                                I::Content(err) => Content(IpAuth(err)),
+                            }
+                        })?;
+                        next_protocol = header.next_header;
+                        result.auth = Some(header);
+                    }
+                }
+                _ => {
+                    // done parsing, the next header is not a known header extension
+                    return Ok((result, next_protocol));
+                }
+            }
+        }
+
+        //should not be hit
+    }
+
+    /// Writes the given headers to a writer based on the order defined in
+    /// the next_header fields of the headers and the first header_id
+    /// passed to this function.
+    ///
+    /// It is required that all next header are correctly set in the headers
+    /// and no other ipv6 header extensions follow this header. If this is not
+    /// the case an [`err::ipv6_exts::HeaderWriteError::Content`] error is
+    /// returned.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn write<T: std::io::Write + Sized>(
+        &self,
+        writer: &mut T,
+        first_header: IpNumber,
+    ) -> Result<(), err::ipv6_exts::HeaderWriteError> {
+        use err::ipv6_exts::ExtsWalkError::*;
+        use err::ipv6_exts::HeaderWriteError::*;
+        use ip_number::*;
+
+        /// Struct flagging if a header needs to be written.
+        struct NeedsWrite {
+            pub hop_by_hop_options: bool,
+            pub destination_options: bool,
+            pub routing: bool,
+            pub fragment: bool,
+            pub auth: bool,
+            pub final_destination_options: bool,
+        }
+
+        let mut needs_write = NeedsWrite {
+            hop_by_hop_options: self.hop_by_hop_options.is_some(),
+            destination_options: self.destination_options.is_some(),
+            routing: self.routing.is_some(),
+            fragment: self.fragment.is_some(),
+            auth: self.auth.is_some(),
+            final_destination_options: if let Some(ref routing) = self.routing {
+                routing.final_destination_options.is_some()
+            } else {
+                false
+            },
+        };
+
+        let mut next_header = first_header;
+        let mut route_written = false;
+
+        // check if hop by hop header should be written first
+        if IPV6_HOP_BY_HOP == next_header {
+            let header = &self.hop_by_hop_options.as_ref().unwrap();
+            header.write(writer).map_err(Io)?;
+            next_header = header.next_header;
+            needs_write.hop_by_hop_options = false;
+        }
+
+        loop {
+            match next_header {
+                IPV6_HOP_BY_HOP => {
+                    // Only trigger a "hop by hop not at start" error
+                    // if we actually still have to write a hop by hop header.
+                    //
+                    // The ip number for hop by hop is 0, which could be used
+                    // as a placeholder by user and later replaced. So let's
+                    // not be overzealous and allow a next header with hop
+                    // by hop if it is not part of this extensions struct.
+                    if needs_write.hop_by_hop_options {
+                        // the hop by hop header is only allowed at the start
+                        return Err(Content(HopByHopNotAtStart));
+                    } else {
+                        break;
+                    }
+                }
+                IPV6_DEST_OPTIONS => {
+                    // the destination options are allowed to be written twice
+                    // once before a routing header and once after.
+                    if route_written {
+                        if needs_write.final_destination_options {
+                            let header = &self
+                                .routing
+                                .as_ref()
+                                .unwrap()
+                                .final_destination_options
+                                .as_ref()
+                                .unwrap();
+                            header.write(writer).map_err(Io)?;
+                            next_header = header.next_header;
+                            needs_write.final_destination_options = false;
+                        } else {
+                            break;
+                        }
+                    } else if needs_write.destination_options {
+                        let header = &self.destination_options.as_ref().unwrap();
+                        header.write(writer).map_err(Io)?;
+                        next_header = header.next_header;
+                        needs_write.destination_options = false;
+                    } else {
+                        break;
+                    }
+                }
+                IPV6_ROUTE => {
+                    if needs_write.routing {
+                        let header = &self.routing.as_ref().unwrap().routing;
+                        header.write(writer).map_err(Io)?;
+                        next_header = header.next_header;
+                        needs_write.routing = false;
+                        // for destination options
+                        route_written = true;
+                    } else {
+                        break;
+                    }
+                }
+                IPV6_FRAG => {
+                    if needs_write.fragment {
+                        let header = &self.fragment.as_ref().unwrap();
+                        header.write(writer).map_err(Io)?;
+                        next_header = header.next_header;
+                        needs_write.fragment = false;
+                    } else {
+                        break;
+                    }
+                }
+                AUTH => {
+                    if needs_write.auth {
+                        let header = &self.auth.as_ref().unwrap();
+                        header.write(writer).map_err(Io)?;
+                        next_header = header.next_header;
+                        needs_write.auth = false;
+                    } else {
+                        break;
+                    }
+                }
+                _ => {
+                    // reached an unknown next_header id, proceed to check if everything was written
+                    break;
+                }
+            }
+        }
+
+        // check that all header have been written
+        if needs_write.hop_by_hop_options {
+            Err(Content(ExtNotReferenced {
+                missing_ext: IpNumber::IPV6_HEADER_HOP_BY_HOP,
+            }))
+        } else if needs_write.destination_options {
+            Err(Content(ExtNotReferenced {
+                missing_ext: IpNumber::IPV6_DESTINATION_OPTIONS,
+            }))
+        } else if needs_write.routing {
+            Err(Content(ExtNotReferenced {
+                missing_ext: IpNumber::IPV6_ROUTE_HEADER,
+            }))
+        } else if needs_write.fragment {
+            Err(Content(ExtNotReferenced {
+                missing_ext: IpNumber::IPV6_FRAGMENTATION_HEADER,
+            }))
+        } else if needs_write.auth {
+            Err(Content(ExtNotReferenced {
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            }))
+        } else if needs_write.final_destination_options {
+            Err(Content(ExtNotReferenced {
+                missing_ext: IpNumber::IPV6_DESTINATION_OPTIONS,
+            }))
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Length of the all present headers in bytes.
+    pub fn header_len(&self) -> usize {
+        let mut result = 0;
+
+        if let Some(ref header) = self.hop_by_hop_options {
+            result += header.header_len();
+        }
+        if let Some(ref header) = self.destination_options {
+            result += header.header_len();
+        }
+        if let Some(ref header) = self.routing {
+            result += header.routing.header_len();
+            if let Some(ref header) = header.final_destination_options {
+                result += header.header_len();
+            }
+        }
+        if let Some(ref header) = self.fragment {
+            result += header.header_len();
+        }
+        if let Some(ref header) = self.auth {
+            result += header.header_len();
+        }
+
+        result
+    }
+
+    /// Sets all the next_header fields of the headers based on the adviced default order
+    /// with the given protocol number as last "next header" value. The return value is the protocol
+    /// number of the first existing extension header that should be entered in the ipv6 header as
+    /// next_header.
+    ///
+    /// If no extension headers are present the value of the argument is returned.
+    pub fn set_next_headers(&mut self, last_protocol_number: IpNumber) -> IpNumber {
+        use ip_number::*;
+
+        let mut next = last_protocol_number;
+
+        // go through the proposed order of extension headers from
+        // RFC 8200 backwards. The header order defined in RFC8200 is:
+        //
+        // * IPv6 header
+        // * Hop-by-Hop Options header
+        // * Destination Options header
+        // * Routing header
+        // * Fragment header
+        // * Authentication header
+        // * Encapsulating Security Payload header
+        // * Destination Options header
+        // * Upper-Layer header
+        //
+        if let Some(ref mut routing) = self.routing {
+            if let Some(ref mut header) = routing.final_destination_options {
+                header.next_header = next;
+                next = IPV6_DEST_OPTIONS;
+            }
+        }
+        if let Some(ref mut header) = self.auth {
+            header.next_header = next;
+            next = AUTH;
+        }
+        if let Some(ref mut header) = self.fragment {
+            header.next_header = next;
+            next = IPV6_FRAG;
+        }
+        if let Some(ref mut routing) = self.routing {
+            routing.routing.next_header = next;
+            next = IPV6_ROUTE;
+        }
+        if let Some(ref mut header) = self.destination_options {
+            header.next_header = next;
+            next = IPV6_DEST_OPTIONS;
+        }
+        if let Some(ref mut header) = self.hop_by_hop_options {
+            header.next_header = next;
+            next = IPV6_HOP_BY_HOP;
+        }
+
+        next
+    }
+
+    /// Return next header based on the extension headers and
+    /// the first ip protocol number.
+    pub fn next_header(&self, first_next_header: IpNumber) -> Result<IpNumber, ExtsWalkError> {
+        use ip_number::*;
+        use ExtsWalkError::*;
+
+        /// Struct flagging if a header needs to be referenced.
+        struct OutstandingRef {
+            pub hop_by_hop_options: bool,
+            pub destination_options: bool,
+            pub routing: bool,
+            pub fragment: bool,
+            pub auth: bool,
+            pub final_destination_options: bool,
+        }
+
+        let mut outstanding_refs = OutstandingRef {
+            hop_by_hop_options: self.hop_by_hop_options.is_some(),
+            destination_options: self.destination_options.is_some(),
+            routing: self.routing.is_some(),
+            fragment: self.fragment.is_some(),
+            auth: self.auth.is_some(),
+            final_destination_options: if let Some(ref routing) = self.routing {
+                routing.final_destination_options.is_some()
+            } else {
+                false
+            },
+        };
+
+        let mut next = first_next_header;
+        let mut route_refed = false;
+
+        // check if hop by hop header should be written first
+        if IPV6_HOP_BY_HOP == next {
+            if let Some(ref header) = self.hop_by_hop_options {
+                next = header.next_header;
+                outstanding_refs.hop_by_hop_options = false;
+            }
+        }
+
+        loop {
+            match next {
+                IPV6_HOP_BY_HOP => {
+                    // Only trigger a "hop by hop not at start" error
+                    // if we actually still have to write a hop by hop header.
+                    //
+                    // The ip number for hop by hop is 0, which could be used
+                    // as a placeholder by user and later replaced. So let's
+                    // not be overzealous and allow a next header with hop
+                    // by hop if it is not part of this extensions struct.
+                    if outstanding_refs.hop_by_hop_options {
+                        // the hop by hop header is only allowed at the start
+                        return Err(HopByHopNotAtStart);
+                    } else {
+                        break;
+                    }
+                }
+                IPV6_DEST_OPTIONS => {
+                    // the destination options are allowed to be written twice
+                    // once before a routing header and once after.
+                    if route_refed {
+                        if outstanding_refs.final_destination_options {
+                            let header = &self
+                                .routing
+                                .as_ref()
+                                .unwrap()
+                                .final_destination_options
+                                .as_ref()
+                                .unwrap();
+                            next = header.next_header;
+                            outstanding_refs.final_destination_options = false;
+                        } else {
+                            break;
+                        }
+                    } else if outstanding_refs.destination_options {
+                        let header = &self.destination_options.as_ref().unwrap();
+                        next = header.next_header;
+                        outstanding_refs.destination_options = false;
+                    } else {
+                        break;
+                    }
+                }
+                IPV6_ROUTE => {
+                    if outstanding_refs.routing {
+                        let header = &self.routing.as_ref().unwrap().routing;
+                        next = header.next_header;
+                        outstanding_refs.routing = false;
+                        // for destination options
+                        route_refed = true;
+                    } else {
+                        break;
+                    }
+                }
+                IPV6_FRAG => {
+                    if outstanding_refs.fragment {
+                        let header = &self.fragment.as_ref().unwrap();
+                        next = header.next_header;
+                        outstanding_refs.fragment = false;
+                    } else {
+                        break;
+                    }
+                }
+                AUTH => {
+                    if outstanding_refs.auth {
+                        let header = &self.auth.as_ref().unwrap();
+                        next = header.next_header;
+                        outstanding_refs.auth = false;
+                    } else {
+                        break;
+                    }
+                }
+                _ => break,
+            }
+        }
+
+        // assume all done
+        if outstanding_refs.hop_by_hop_options {
+            return Err(ExtNotReferenced {
+                missing_ext: IpNumber::IPV6_HEADER_HOP_BY_HOP,
+            });
+        }
+        if outstanding_refs.destination_options {
+            return Err(ExtNotReferenced {
+                missing_ext: IpNumber::IPV6_DESTINATION_OPTIONS,
+            });
+        }
+        if outstanding_refs.routing {
+            return Err(ExtNotReferenced {
+                missing_ext: IpNumber::IPV6_ROUTE_HEADER,
+            });
+        }
+        if outstanding_refs.fragment {
+            return Err(ExtNotReferenced {
+                missing_ext: IpNumber::IPV6_FRAGMENTATION_HEADER,
+            });
+        }
+        if outstanding_refs.auth {
+            return Err(ExtNotReferenced {
+                missing_ext: IpNumber::AUTHENTICATION_HEADER,
+            });
+        }
+        if outstanding_refs.final_destination_options {
+            return Err(ExtNotReferenced {
+                missing_ext: IpNumber::IPV6_DESTINATION_OPTIONS,
+            });
+        }
+
+        Ok(next)
+    }
+
+    /// Returns true if a fragmentation header is present in
+    /// the extensions that fragments the payload.
+    ///
+    /// Note: A fragmentation header can still be present
+    /// even if the return value is false in case the fragmentation
+    /// headers don't fragment the payload. This is the case if
+    /// the offset of all fragmentation header is 0 and the
+    /// more fragment bit is not set.
+    #[inline]
+    pub fn is_fragmenting_payload(&self) -> bool {
+        if let Some(frag) = self.fragment.as_ref() {
+            frag.is_fragmenting_payload()
+        } else {
+            false
+        }
+    }
+
+    /// Returns true if no IPv6 extension header is present (all fields `None`).
+    #[inline]
+    pub fn is_empty(&self) -> bool {
+        self.hop_by_hop_options.is_none()
+            && self.destination_options.is_none()
+            && self.routing.is_none()
+            && self.fragment.is_none()
+            && self.auth.is_none()
+    }
+}
+
+#[cfg(test)]
+pub mod ipv6_exts_test_helpers {
+    use super::*;
+    use crate::ip_number::*;
+    use alloc::vec::Vec;
+
+    /// IP numbers that are assigned ipv6 header extensions.
+    pub const EXTENSION_KNOWN_IP_NUMBERS: [IpNumber; 5] = [
+        AUTH,
+        IPV6_DEST_OPTIONS,
+        IPV6_HOP_BY_HOP,
+        IPV6_FRAG,
+        IPV6_ROUTE,
+    ];
+
+    /// Helper struct that generates test data with dummy
+    /// extension header data.
+    pub struct ExtensionTestPayload {
+        pub ip_numbers: Vec<IpNumber>,
+        pub lengths: Vec<usize>,
+        pub data: Vec<u8>,
+    }
+
+    impl ExtensionTestPayload {
+        pub fn new(ip_numbers: &[IpNumber], header_sizes: &[u8]) -> ExtensionTestPayload {
+            assert!(ip_numbers.len() > 1);
+            assert!(header_sizes.len() > 0);
+
+            let mut result = ExtensionTestPayload {
+                ip_numbers: ip_numbers.to_vec(),
+                lengths: Vec::with_capacity(ip_numbers.len() - 1),
+                data: Vec::with_capacity((ip_numbers.len() - 1) * (0xff * 8 + 8)),
+            };
+            for i in 0..ip_numbers.len() - 1 {
+                result.add_payload(
+                    ip_numbers[i],
+                    ip_numbers[i + 1],
+                    header_sizes[i % header_sizes.len()],
+                )
+            }
+            result
+        }
+
+        pub fn slice(&self) -> &[u8] {
+            &self.data
+        }
+
+        fn add_payload(&mut self, ip_number: IpNumber, next_header: IpNumber, header_ext_len: u8) {
+            match ip_number {
+                IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_DEST_OPTIONS => {
+                    // insert next header & size
+                    let mut raw: [u8; 0xff * 8 + 8] = [0; 0xff * 8 + 8];
+                    raw[0] = next_header.0;
+                    raw[1] = header_ext_len;
+
+                    // insert payload
+                    self.data
+                        .extend_from_slice(&raw[..8 + usize::from(header_ext_len) * 8]);
+                    self.lengths.push(8 + usize::from(header_ext_len) * 8);
+                }
+                IPV6_FRAG => {
+                    // generate payload
+                    let mut raw: [u8; 8] = [0; 8];
+                    raw[0] = next_header.0;
+                    raw[1] = 0;
+
+                    // insert payload
+                    self.data.extend_from_slice(&raw[..8]);
+                    self.lengths.push(8);
+                }
+                AUTH => {
+                    let mut raw: [u8; 0xff * 4 + 8] = [0; 0xff * 4 + 8];
+                    raw[0] = next_header.0;
+                    // authentfication header len is defined as
+                    // '32-bit words (4-byteunits), minus "2"'
+                    let len = if header_ext_len > 0 {
+                        raw[1] = header_ext_len;
+                        usize::from(header_ext_len) * 4
+                    } else {
+                        // auth has a minimum size of 1
+                        raw[1] = 1;
+                        4
+                    } + 8;
+                    self.data.extend_from_slice(&raw[..len]);
+                    self.lengths.push(len);
+                }
+                _ => unreachable!(),
+            }
+        }
+
+        /// Returns true of the payload will trigger a "hop by hop not
+        /// at start" error which is not ignored because of an early
+        /// parsing abort.
+        pub fn exts_hop_by_hop_error(&self) -> bool {
+            struct ReadState {
+                dest_opt: bool,
+                routing: bool,
+                final_dest_opt: bool,
+                frag: bool,
+                auth: bool,
+            }
+
+            // state if a header type has already been read
+            let mut read = ReadState {
+                dest_opt: false,
+                routing: false,
+                final_dest_opt: false,
+                frag: false,
+                auth: false,
+            };
+
+            for i in 0..self.ip_numbers.len() {
+                match self.ip_numbers[i] {
+                    IPV6_HOP_BY_HOP => {
+                        if i != 0 {
+                            return true;
+                        }
+                    }
+                    IPV6_ROUTE => {
+                        if read.routing {
+                            return false;
+                        } else {
+                            read.routing = true;
+                        }
+                    }
+                    IPV6_DEST_OPTIONS => {
+                        // check the kind of destination options (aka is it before or after the routing header)
+                        if read.routing {
+                            // final dest opt
+                            if read.final_dest_opt {
+                                return false;
+                            } else {
+                                read.final_dest_opt = true;
+                            }
+                        } else {
+                            // dst opt
+                            if read.dest_opt {
+                                return false;
+                            } else {
+                                read.dest_opt = true;
+                            }
+                        }
+                    }
+                    IPV6_FRAG => {
+                        if read.frag {
+                            return false;
+                        } else {
+                            read.frag = true;
+                        }
+                    }
+                    AUTH => {
+                        if read.auth {
+                            return false;
+                        } else {
+                            read.auth = true;
+                        }
+                    }
+                    _ => return false,
+                }
+            }
+            return false;
+        }
+
+        /// Checks the if the extensions match the expected values based
+        /// on this test payload.
+        pub fn assert_extensions(
+            &self,
+            exts: &Ipv6Extensions,
+        ) -> (usize, Option<IpNumber>, IpNumber) {
+            struct ReadState {
+                hop_by_hop: bool,
+                dest_opt: bool,
+                routing: bool,
+                final_dest_opt: bool,
+                frag: bool,
+                auth: bool,
+            }
+
+            // state if a header type has already been read
+            let mut read = ReadState {
+                hop_by_hop: false,
+                dest_opt: false,
+                routing: false,
+                final_dest_opt: false,
+                frag: false,
+                auth: false,
+            };
+
+            let mut slice = &self.data[..];
+            let mut last_decoded = None;
+            let mut post_header = self.ip_numbers[0];
+
+            for i in 0..self.ip_numbers.len() - 1 {
+                let mut stop = false;
+                match self.ip_numbers[i] {
+                    IPV6_HOP_BY_HOP => {
+                        assert!(false == read.hop_by_hop);
+                        let (header, rest) = Ipv6RawExtHeader::from_slice(slice).unwrap();
+                        assert_eq!(&header, exts.hop_by_hop_options.as_ref().unwrap());
+                        slice = rest;
+                        read.hop_by_hop = true;
+                        last_decoded = Some(IPV6_HOP_BY_HOP);
+                    }
+                    IPV6_ROUTE => {
+                        if read.routing {
+                            stop = true;
+                        } else {
+                            let (header, rest) = Ipv6RawExtHeader::from_slice(slice).unwrap();
+                            assert_eq!(&header, &exts.routing.as_ref().unwrap().routing);
+                            slice = rest;
+                            read.routing = true;
+                            last_decoded = Some(IPV6_ROUTE);
+                        }
+                    }
+                    IPV6_DEST_OPTIONS => {
+                        // check the kind of destination options (aka is it before or after the routing header)
+                        if read.routing {
+                            // final dest opt
+                            if read.final_dest_opt {
+                                stop = true;
+                            } else {
+                                let (header, rest) = Ipv6RawExtHeader::from_slice(slice).unwrap();
+                                assert_eq!(
+                                    &header,
+                                    exts.routing
+                                        .as_ref()
+                                        .unwrap()
+                                        .final_destination_options
+                                        .as_ref()
+                                        .unwrap()
+                                );
+                                slice = rest;
+                                read.final_dest_opt = true;
+                                last_decoded = Some(IPV6_DEST_OPTIONS);
+                            }
+                        } else {
+                            // dst opt
+                            if read.dest_opt {
+                                stop = true;
+                            } else {
+                                let (header, rest) = Ipv6RawExtHeader::from_slice(slice).unwrap();
+                                assert_eq!(&header, exts.destination_options.as_ref().unwrap());
+                                slice = rest;
+                                read.dest_opt = true;
+                                last_decoded = Some(IPV6_DEST_OPTIONS);
+                            }
+                        }
+                    }
+                    IPV6_FRAG => {
+                        if read.frag {
+                            // duplicate header -> stop
+                            stop = true;
+                        } else {
+                            let (header, rest) = Ipv6FragmentHeader::from_slice(slice).unwrap();
+                            assert_eq!(&header, exts.fragment.as_ref().unwrap());
+                            slice = rest;
+                            read.frag = true;
+                            last_decoded = Some(IPV6_FRAG);
+                        }
+                    }
+                    AUTH => {
+                        if read.auth {
+                            // duplicate header -> stop
+                            stop = true;
+                        } else {
+                            let (header, rest) = IpAuthHeader::from_slice(slice).unwrap();
+                            assert_eq!(&header, exts.auth.as_ref().unwrap());
+                            slice = rest;
+                            read.auth = true;
+                            last_decoded = Some(AUTH);
+                        }
+                    }
+                    _ => {
+                        // non extension header -> stop
+                        stop = true;
+                    }
+                }
+                if stop {
+                    post_header = self.ip_numbers[i];
+                    break;
+                } else {
+                    post_header = self.ip_numbers[i + 1];
+                }
+            }
+
+            // check the non parsed headers are not present
+            if false == read.hop_by_hop {
+                assert!(exts.hop_by_hop_options.is_none());
+            }
+            if false == read.dest_opt {
+                assert!(exts.destination_options.is_none());
+            }
+            if false == read.routing {
+                assert!(exts.routing.is_none());
+            } else {
+                if false == read.final_dest_opt {
+                    assert!(exts
+                        .routing
+                        .as_ref()
+                        .unwrap()
+                        .final_destination_options
+                        .is_none());
+                }
+            }
+            if false == read.frag {
+                assert!(exts.fragment.is_none());
+            }
+            if false == read.auth {
+                assert!(exts.auth.is_none());
+            }
+
+            (self.data.len() - slice.len(), last_decoded, post_header)
+        }
+
+        /// Return the expected lax from slice result and ipnumber which caused
+        /// an error.
+        pub fn lax_extensions_for_len(
+            &self,
+            limiting_len: usize,
+        ) -> (Ipv6Extensions, IpNumber, &[u8], Option<IpNumber>) {
+            // state if a header type has already been read
+            let mut exts: Ipv6Extensions = Default::default();
+            let mut post_header = *self.ip_numbers.first().unwrap();
+            let mut slice = &self.data[..];
+
+            for i in 0..self.ip_numbers.len() - 1 {
+                // check if the limiting size gets hit
+                if self.slice().len() - slice.len() + self.lengths[i] > limiting_len {
+                    return (
+                        exts,
+                        self.ip_numbers[i],
+                        &self.slice()[self.slice().len() - slice.len()..limiting_len],
+                        Some(self.ip_numbers[i]),
+                    );
+                }
+
+                let mut stop = false;
+                match self.ip_numbers[i] {
+                    IPV6_HOP_BY_HOP => {
+                        assert!(exts.hop_by_hop_options.is_none());
+                        let (header, rest) = Ipv6RawExtHeader::from_slice(slice).unwrap();
+                        exts.hop_by_hop_options = Some(header);
+                        slice = rest;
+                    }
+                    IPV6_ROUTE => {
+                        if exts.routing.is_some() {
+                            stop = true;
+                        } else {
+                            let (header, rest) = Ipv6RawExtHeader::from_slice(slice).unwrap();
+                            exts.routing = Some(Ipv6RoutingExtensions {
+                                routing: header,
+                                final_destination_options: None,
+                            });
+                            slice = rest;
+                        }
+                    }
+                    IPV6_DEST_OPTIONS => {
+                        // check the kind of destination options (aka is it before or after the routing header)
+                        if let Some(routing) = exts.routing.as_mut() {
+                            // final dest opt
+                            if routing.final_destination_options.is_some() {
+                                stop = true;
+                            } else {
+                                let (header, rest) = Ipv6RawExtHeader::from_slice(slice).unwrap();
+                                routing.final_destination_options = Some(header);
+                                slice = rest;
+                            }
+                        } else {
+                            // dst opt
+                            if exts.destination_options.is_some() {
+                                stop = true;
+                            } else {
+                                let (header, rest) = Ipv6RawExtHeader::from_slice(slice).unwrap();
+                                exts.destination_options = Some(header);
+                                slice = rest;
+                            }
+                        }
+                    }
+                    IPV6_FRAG => {
+                        if exts.fragment.is_some() {
+                            // duplicate header -> stop
+                            stop = true;
+                        } else {
+                            let (header, rest) = Ipv6FragmentHeader::from_slice(slice).unwrap();
+                            exts.fragment = Some(header);
+                            slice = rest;
+                        }
+                    }
+                    AUTH => {
+                        if exts.auth.is_some() {
+                            // duplicate header -> stop
+                            stop = true;
+                        } else {
+                            let (header, rest) = IpAuthHeader::from_slice(slice).unwrap();
+                            exts.auth = Some(header);
+                            slice = rest;
+                        }
+                    }
+                    _ => {
+                        // non extension header -> stop
+                        stop = true;
+                    }
+                }
+                if stop {
+                    post_header = self.ip_numbers[i];
+                    break;
+                } else {
+                    post_header = self.ip_numbers[i + 1];
+                }
+            }
+
+            (
+                exts,
+                post_header,
+                &self.slice()[self.slice().len() - slice.len()..limiting_len],
+                None,
+            )
+        }
+    }
+
+    /// extension header data.
+    #[derive(Clone)]
+    pub struct ExtensionTestHeaders {
+        pub ip_numbers: Vec<IpNumber>,
+        pub data: Ipv6Extensions,
+    }
+
+    impl ExtensionTestHeaders {
+        pub fn new(ip_numbers: &[IpNumber], header_sizes: &[u8]) -> ExtensionTestHeaders {
+            assert!(ip_numbers.len() > 1);
+            assert!(header_sizes.len() > 0);
+
+            let mut result = ExtensionTestHeaders {
+                ip_numbers: ip_numbers.to_vec(),
+                data: Default::default(),
+            };
+            for i in 0..ip_numbers.len() - 1 {
+                let succ = result.add_payload(
+                    ip_numbers[i],
+                    ip_numbers[i + 1],
+                    header_sizes[i % header_sizes.len()],
+                );
+                if false == succ {
+                    // write was not possible (duplicate)
+                    // reduce the list so the current ip number
+                    // is the final one
+                    result.ip_numbers.truncate(i + 1);
+                    break;
+                }
+            }
+            result
+        }
+
+        pub fn introduce_missing_ref(&mut self, new_header: IpNumber) -> IpNumber {
+            assert!(self.ip_numbers.len() >= 2);
+
+            // set the next_header of the last extension header and return the id
+            if self.ip_numbers.len() >= 3 {
+                match self.ip_numbers[self.ip_numbers.len() - 3] {
+                    IPV6_HOP_BY_HOP => {
+                        self.data.hop_by_hop_options.as_mut().unwrap().next_header = new_header;
+                    }
+                    IPV6_DEST_OPTIONS => {
+                        if self.ip_numbers[..self.ip_numbers.len() - 3]
+                            .iter()
+                            .any(|&x| x == IPV6_ROUTE)
+                        {
+                            self.data
+                                .routing
+                                .as_mut()
+                                .unwrap()
+                                .final_destination_options
+                                .as_mut()
+                                .unwrap()
+                                .next_header = new_header;
+                        } else {
+                            self.data.destination_options.as_mut().unwrap().next_header =
+                                new_header;
+                        }
+                    }
+                    IPV6_ROUTE => {
+                        self.data.routing.as_mut().unwrap().routing.next_header = new_header;
+                    }
+                    IPV6_FRAG => {
+                        self.data.fragment.as_mut().unwrap().next_header = new_header;
+                    }
+                    AUTH => {
+                        self.data.auth.as_mut().unwrap().next_header = new_header;
+                    }
+                    _ => unreachable!(),
+                }
+                match self.ip_numbers[self.ip_numbers.len() - 2] {
+                    IPV6_HOP_BY_HOP => IpNumber::IPV6_HEADER_HOP_BY_HOP,
+                    IPV6_DEST_OPTIONS => IpNumber::IPV6_DESTINATION_OPTIONS,
+                    IPV6_ROUTE => IpNumber::IPV6_ROUTE_HEADER,
+                    IPV6_FRAG => IpNumber::IPV6_FRAGMENTATION_HEADER,
+                    AUTH => IpNumber::AUTHENTICATION_HEADER,
+                    _ => unreachable!(),
+                }
+            } else {
+                // rewrite start number in case it is just one extension header
+                let missing = self.ip_numbers[0];
+                self.ip_numbers[0] = new_header;
+                match missing {
+                    IPV6_HOP_BY_HOP => IpNumber::IPV6_HEADER_HOP_BY_HOP,
+                    IPV6_DEST_OPTIONS => IpNumber::IPV6_DESTINATION_OPTIONS,
+                    IPV6_ROUTE => IpNumber::IPV6_ROUTE_HEADER,
+                    IPV6_FRAG => IpNumber::IPV6_FRAGMENTATION_HEADER,
+                    AUTH => IpNumber::AUTHENTICATION_HEADER,
+                    _ => unreachable!(),
+                }
+            }
+        }
+
+        fn add_payload(
+            &mut self,
+            ip_number: IpNumber,
+            next_header: IpNumber,
+            header_ext_len: u8,
+        ) -> bool {
+            match ip_number {
+                IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_DEST_OPTIONS => {
+                    use Ipv6RawExtHeader as R;
+                    let payload: [u8; R::MAX_PAYLOAD_LEN] = [0; R::MAX_PAYLOAD_LEN];
+                    let len = usize::from(header_ext_len) * 8 + 6;
+
+                    let raw = Ipv6RawExtHeader::new_raw(next_header, &payload[..len]).unwrap();
+                    match ip_number {
+                        IPV6_HOP_BY_HOP => {
+                            if self.data.hop_by_hop_options.is_none() {
+                                self.data.hop_by_hop_options = Some(raw);
+                                true
+                            } else {
+                                false
+                            }
+                        }
+                        IPV6_ROUTE => {
+                            if self.data.routing.is_none() {
+                                self.data.routing = Some(Ipv6RoutingExtensions {
+                                    routing: raw,
+                                    final_destination_options: None,
+                                });
+                                true
+                            } else {
+                                false
+                            }
+                        }
+                        IPV6_DEST_OPTIONS => {
+                            if let Some(ref mut route) = self.data.routing {
+                                if route.final_destination_options.is_none() {
+                                    route.final_destination_options = Some(raw);
+                                    true
+                                } else {
+                                    false
+                                }
+                            } else {
+                                // dest option
+                                if self.data.destination_options.is_none() {
+                                    self.data.destination_options = Some(raw);
+                                    true
+                                } else {
+                                    false
+                                }
+                            }
+                        }
+                        _ => unreachable!(),
+                    }
+                }
+                IPV6_FRAG => {
+                    if self.data.fragment.is_none() {
+                        self.data.fragment = Some(Ipv6FragmentHeader::new(
+                            next_header,
+                            IpFragOffset::ZERO,
+                            true,
+                            123,
+                        ));
+                        true
+                    } else {
+                        false
+                    }
+                }
+                AUTH => {
+                    if self.data.auth.is_none() {
+                        use IpAuthHeader as A;
+
+                        let mut len = usize::from(header_ext_len) * 4;
+                        if len > A::MAX_ICV_LEN {
+                            len = A::MAX_ICV_LEN;
+                        }
+                        let raw_icv: [u8; A::MAX_ICV_LEN] = [0; A::MAX_ICV_LEN];
+                        self.data.auth = Some(
+                            IpAuthHeader::new(next_header, 123, 234, &raw_icv[..len]).unwrap(),
+                        );
+                        true
+                    } else {
+                        false
+                    }
+                }
+                _ => unreachable!(),
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::ipv6_exts_test_helpers::*;
+    use super::*;
+    use crate::ip_number::*;
+    use crate::test_gens::*;
+    use alloc::{borrow::ToOwned, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            header_size in any::<u8>(),
+            post_header in ip_number_any()
+                .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(),
+                    |v| !EXTENSION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x)
+                )
+        ) {
+            use err::ipv6_exts::{HeaderError::*, HeaderSliceError::*};
+
+            // no extension headers filled
+            {
+                let some_data = [1,2,3,4];
+                let actual = Ipv6Extensions::from_slice(post_header, &some_data).unwrap();
+                assert_eq!(actual.0, Default::default());
+                assert_eq!(actual.1, post_header);
+                assert_eq!(actual.2, &some_data);
+            }
+
+            /// Run a test with the given ip numbers
+            fn run_test(ip_numbers: &[IpNumber], header_sizes: &[u8]) {
+                // setup test payload
+                let e = ExtensionTestPayload::new(
+                    ip_numbers,
+                    header_sizes
+                );
+
+                if e.exts_hop_by_hop_error() {
+                    // a hop by hop header that is not at the start triggers an error
+                    assert_eq!(
+                        Ipv6Extensions::from_slice(ip_numbers[0], e.slice()).unwrap_err(),
+                        Content(HopByHopNotAtStart)
+                    );
+                } else {
+                    // normal read
+                    let (header, next, rest) = Ipv6Extensions::from_slice(ip_numbers[0], e.slice()).unwrap();
+                    let (read_len, last_header, expected_post_header) = e.assert_extensions(&header);
+                    assert_eq!(next, expected_post_header);
+                    assert_eq!(rest, &e.slice()[read_len..]);
+
+                    // unexpected end of slice
+                    {
+                        let mut offset: usize = 0;
+                        for l in &e.lengths {
+                            if offset + l >= read_len {
+                                break;
+                            }
+                            offset += l;
+                        }
+
+                        assert_eq!(
+                            Ipv6Extensions::from_slice(ip_numbers[0], &e.slice()[..read_len - 1]).unwrap_err(),
+                            Len(err::LenError {
+                                required_len: read_len - offset,
+                                len: read_len - offset - 1,
+                                len_source: LenSource::Slice,
+                                layer: match last_header.unwrap() {
+                                    AUTH => err::Layer::IpAuthHeader,
+                                    IPV6_FRAG => err::Layer::Ipv6FragHeader,
+                                    _ => err::Layer::Ipv6ExtHeader
+                                },
+                                layer_start_offset: offset,
+                            })
+                        );
+                    }
+                }
+            }
+
+            // test the parsing of different extension header combinations
+            for first_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                // single header parsing
+                run_test(
+                    &[*first_header, post_header],
+                    &[header_size],
+                );
+
+                for second_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                    // double header parsing
+                    run_test(
+                        &[*first_header, *second_header, post_header],
+                        &[header_size],
+                    );
+
+                    for third_header in &EXTENSION_KNOWN_IP_NUMBERS {
+                        // tripple header parsing
+                        run_test(
+                            &[*first_header, *second_header, *third_header, post_header],
+                            &[header_size],
+                        );
+                    }
+                }
+            }
+
+            // test that the auth content error gets forwarded
+            {
+                let auth = IpAuthHeader::new(post_header, 0, 0, &[]).unwrap();
+                let mut bytes = auth.to_bytes();
+                // inject an invalid len value
+                bytes[1] = 0;
+                let actual = Ipv6Extensions::from_slice(AUTH, &bytes).unwrap_err();
+
+                use err::ipv6_exts::HeaderError::IpAuth;
+                use err::ip_auth::HeaderError::ZeroPayloadLen;
+                assert_eq!(actual, Content(IpAuth(ZeroPayloadLen)));
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice_lax(
+            header_size in any::<u8>(),
+            post_header in ip_number_any()
+                .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(),
+                    |v| !EXTENSION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x)
+                )
+        ) {
+            use err::ipv6_exts::{HeaderError::*, HeaderSliceError::*};
+
+            // no extension headers filled
+            {
+                let some_data = [1,2,3,4];
+                let actual = Ipv6Extensions::from_slice_lax(post_header, &some_data);
+                assert_eq!(actual.0, Default::default());
+                assert_eq!(actual.1, post_header);
+                assert_eq!(actual.2, &some_data);
+                assert!(actual.3.is_none());
+            }
+
+            /// Run a test with the given ip numbers
+            fn run_test(ip_numbers: &[IpNumber], header_sizes: &[u8]) {
+                // setup test payload
+                let e = ExtensionTestPayload::new(
+                    ip_numbers,
+                    header_sizes
+                );
+
+                if e.exts_hop_by_hop_error() {
+                    // a hop by hop header that is not at the start triggers an error
+                    let actual = Ipv6Extensions::from_slice_lax(ip_numbers[0], e.slice());
+                    assert_eq!(actual.3.unwrap(), (Content(HopByHopNotAtStart), Layer::Ipv6HopByHopHeader));
+                } else {
+                    // normal read
+                    let norm_actual = Ipv6Extensions::from_slice_lax(ip_numbers[0], e.slice());
+                    let norm_expected = e.lax_extensions_for_len(e.slice().len());
+                    assert_eq!(norm_actual.0, norm_expected.0);
+                    assert_eq!(norm_actual.1, norm_expected.1);
+                    assert_eq!(norm_actual.2, norm_expected.2);
+                    assert!(norm_actual.3.is_none());
+
+                    // unexpected end of slice
+                    if norm_actual.0.header_len() > 0 {
+
+                        let norm_len = norm_actual.0.header_len();
+                        let actual = Ipv6Extensions::from_slice_lax(ip_numbers[0], &e.slice()[..norm_len - 1]);
+
+                        let expected = e.lax_extensions_for_len(norm_len - 1);
+                        assert_eq!(actual.0, expected.0);
+                        assert_eq!(actual.1, expected.1);
+                        assert_eq!(actual.2, expected.2);
+                        let len_err = actual.3.unwrap().0.len_error().unwrap().clone();
+                        assert_eq!(len_err.len, norm_len - 1 - expected.0.header_len());
+                        assert_eq!(len_err.len_source, LenSource::Slice);
+                        assert_eq!(
+                            len_err.layer,
+                            match expected.3.unwrap() {
+                                AUTH => err::Layer::IpAuthHeader,
+                                IPV6_FRAG => err::Layer::Ipv6FragHeader,
+                                _ => err::Layer::Ipv6ExtHeader
+                            }
+                        );
+                        assert_eq!(len_err.layer_start_offset, expected.0.header_len());
+                    }
+                }
+            }
+
+            // test the parsing of different extension header combinations
+            for first_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                // single header parsing
+                run_test(
+                    &[*first_header, post_header],
+                    &[header_size],
+                );
+
+                for second_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                    // double header parsing
+                    run_test(
+                        &[*first_header, *second_header, post_header],
+                        &[header_size],
+                    );
+
+                    for third_header in &EXTENSION_KNOWN_IP_NUMBERS {
+                        // tripple header parsing
+                        run_test(
+                            &[*first_header, *second_header, *third_header, post_header],
+                            &[header_size],
+                        );
+                    }
+                }
+            }
+
+            // test that the auth content error gets forwarded
+            {
+                let auth = IpAuthHeader::new(post_header, 0, 0, &[]).unwrap();
+                let mut bytes = auth.to_bytes();
+                // inject an invalid len value
+                bytes[1] = 0;
+                let actual = Ipv6Extensions::from_slice_lax(AUTH, &bytes);
+                assert_eq!(0, actual.0.header_len());
+                assert_eq!(AUTH, actual.1);
+                assert_eq!(&bytes[..], actual.2);
+
+                use err::ipv6_exts::HeaderError::IpAuth;
+                use err::ip_auth::HeaderError::ZeroPayloadLen;
+                assert_eq!(actual.3.unwrap(), (Content(IpAuth(ZeroPayloadLen)), Layer::IpAuthHeader));
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read(
+            header_size in any::<u8>(),
+            post_header in ip_number_any()
+                .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(),
+                    |v| !EXTENSION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x)
+                )
+        ) {
+            use err::ipv6_exts::HeaderError::*;
+            use std::io::Cursor;
+
+            // no extension headers filled
+            {
+                let mut cursor = Cursor::new(&[]);
+                let actual = Ipv6Extensions::read(&mut cursor, post_header).unwrap();
+                assert_eq!(actual.0, Default::default());
+                assert_eq!(actual.1, post_header);
+                assert_eq!(0, cursor.position());
+            }
+
+            /// Run a test with the given ip numbers
+            fn run_test(ip_numbers: &[IpNumber], header_sizes: &[u8]) {
+                // setup test payload
+                let e = ExtensionTestPayload::new(
+                    ip_numbers,
+                    header_sizes
+                );
+                let mut cursor = Cursor::new(e.slice());
+
+                if e.exts_hop_by_hop_error() {
+                    // a hop by hop header that is not at the start triggers an error
+                    assert_eq!(
+                        Ipv6Extensions::read(&mut cursor, ip_numbers[0]).unwrap_err().content_error().unwrap(),
+                        HopByHopNotAtStart
+                    );
+                } else {
+                    // normal read
+                    let (header, next) = Ipv6Extensions::read(&mut cursor, ip_numbers[0]).unwrap();
+                    let (read_len, _, expected_post_header) = e.assert_extensions(&header);
+                    assert_eq!(next, expected_post_header);
+                    assert_eq!(cursor.position() as usize, read_len);
+
+                    // unexpected end of slice
+                    {
+                        let mut short_cursor = Cursor::new(&e.slice()[..read_len - 1]);
+                        assert!(
+                            Ipv6Extensions::read(&mut short_cursor, ip_numbers[0])
+                            .unwrap_err()
+                            .io_error()
+                            .is_some()
+                        );
+                    }
+                }
+            }
+
+            // test the parsing of different extension header combinations
+            for first_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                // single header parsing
+                run_test(
+                    &[*first_header, post_header],
+                    &[header_size],
+                );
+
+                for second_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                    // double header parsing
+                    run_test(
+                        &[*first_header, *second_header, post_header],
+                        &[header_size],
+                    );
+
+                    for third_header in &EXTENSION_KNOWN_IP_NUMBERS {
+                        // tripple header parsing
+                        run_test(
+                            &[*first_header, *second_header, *third_header, post_header],
+                            &[header_size],
+                        );
+                    }
+                }
+            }
+
+            // test that the auth content error gets forwarded
+            {
+                let auth = IpAuthHeader::new(post_header, 0, 0, &[]).unwrap();
+                let mut bytes = auth.to_bytes();
+                // inject an invalid len value
+                bytes[1] = 0;
+                let mut cursor = Cursor::new(&bytes[..]);
+                let actual = Ipv6Extensions::read(&mut cursor, AUTH).unwrap_err();
+
+                use err::ipv6_exts::HeaderError::IpAuth;
+                use err::ip_auth::HeaderError::ZeroPayloadLen;
+                assert_eq!(actual.content_error().unwrap(), IpAuth(ZeroPayloadLen));
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read_limited(
+            header_size in any::<u8>(),
+            post_header in ip_number_any()
+                .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(),
+                    |v| !EXTENSION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x)
+                )
+        ) {
+            use err::ipv6_exts::HeaderError::*;
+            use err::Layer;
+            use std::io::Cursor;
+            use crate::io::LimitedReader;
+
+            // no extension headers filled
+            {
+                let mut reader = LimitedReader::new(
+                    Cursor::new(&[]),
+                    0,
+                    LenSource::Slice,
+                    0,
+                    Layer::Ipv6Header
+                );
+                let actual = Ipv6Extensions::read_limited(&mut reader, post_header).unwrap();
+                assert_eq!(actual.0, Default::default());
+                assert_eq!(actual.1, post_header);
+                assert_eq!(0, reader.read_len());
+            }
+
+            /// Run a test with the given ip numbers
+            fn run_test(ip_numbers: &[IpNumber], header_sizes: &[u8]) {
+                // setup test payload
+                let e = ExtensionTestPayload::new(
+                    ip_numbers,
+                    header_sizes
+                );
+                let mut reader = LimitedReader::new(
+                    Cursor::new(e.slice()),
+                    e.slice().len(),
+                    LenSource::Slice,
+                    0,
+                    Layer::Ipv6Header
+                );
+
+                if e.exts_hop_by_hop_error() {
+                    // a hop by hop header that is not at the start triggers an error
+                    assert_eq!(
+                        Ipv6Extensions::read_limited(&mut reader, ip_numbers[0]).unwrap_err().content().unwrap(),
+                        HopByHopNotAtStart
+                    );
+                } else {
+                    // normal read
+                    let (header, next) = Ipv6Extensions::read_limited(&mut reader, ip_numbers[0]).unwrap();
+                    let (read_len, _, expected_post_header) = e.assert_extensions(&header);
+                    assert_eq!(next, expected_post_header);
+                    assert_eq!(reader.read_len() + reader.layer_offset(), read_len);
+
+                    // io error unexpected end
+                    {
+                        let mut short_reader = LimitedReader::new(
+                            Cursor::new(&e.slice()[..read_len - 1]),
+                            read_len,
+                            LenSource::Slice,
+                            0,
+                            Layer::Ipv6Header
+                        );
+
+                        assert!(
+                            Ipv6Extensions::read_limited(&mut short_reader, ip_numbers[0])
+                            .unwrap_err()
+                            .io()
+                            .is_some()
+                        );
+                    }
+
+                    // len error
+                    {
+                        let mut short_reader = LimitedReader::new(
+                            Cursor::new(e.slice()),
+                            read_len - 1,
+                            LenSource::Slice,
+                            0,
+                            Layer::Ipv6Header
+                        );
+
+                        assert!(
+                            Ipv6Extensions::read_limited(&mut short_reader, ip_numbers[0])
+                            .unwrap_err()
+                            .len()
+                            .is_some()
+                        );
+                    }
+                }
+            }
+
+            // test the parsing of different extension header combinations
+            for first_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                // single header parsing
+                run_test(
+                    &[*first_header, post_header],
+                    &[header_size],
+                );
+
+                for second_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                    // double header parsing
+                    run_test(
+                        &[*first_header, *second_header, post_header],
+                        &[header_size],
+                    );
+
+                    for third_header in &EXTENSION_KNOWN_IP_NUMBERS {
+                        // tripple header parsing
+                        run_test(
+                            &[*first_header, *second_header, *third_header, post_header],
+                            &[header_size],
+                        );
+                    }
+                }
+            }
+
+            // test that the auth content error gets forwarded
+            {
+                let auth = IpAuthHeader::new(post_header, 0, 0, &[]).unwrap();
+                let mut bytes = auth.to_bytes();
+                // inject an invalid len value
+                bytes[1] = 0;
+                let mut reader = LimitedReader::new(
+                    Cursor::new(&bytes[..]),
+                    bytes.len(),
+                    LenSource::Slice,
+                    0,
+                    Layer::Ipv6Header
+                );
+                let actual = Ipv6Extensions::read_limited(&mut reader, AUTH).unwrap_err();
+
+                use err::ipv6_exts::HeaderError::IpAuth;
+                use err::ip_auth::HeaderError::ZeroPayloadLen;
+                assert_eq!(actual.content().unwrap(), IpAuth(ZeroPayloadLen));
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write(
+            header_size in any::<u8>(),
+            post_header in ip_number_any()
+                .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(),
+                    |v| !EXTENSION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x)
+                )
+        ) {
+            // no extension headers filled
+            {
+                let exts : Ipv6Extensions = Default::default();
+                let mut buffer = Vec::new();
+                exts.write(&mut buffer, post_header).unwrap();
+                assert_eq!(0, buffer.len());
+            }
+
+            /// Run a test with the given ip numbers
+            fn run_test(ip_numbers: &[IpNumber], header_sizes: &[u8], post_header: IpNumber) {
+                use std::io::Cursor;
+                use crate::err::ipv6_exts::ExtsWalkError::*;
+
+                // setup test header
+                let e = ExtensionTestHeaders::new(
+                    ip_numbers,
+                    header_sizes
+                );
+
+                if e.ip_numbers[1..e.ip_numbers.len()-1].iter().any(|&x| x == IPV6_HOP_BY_HOP) {
+                    // a hop by hop header that is not at the start triggers an error
+                    let mut writer = Vec::with_capacity(e.data.header_len());
+                    assert_eq!(
+                        e.data.write(&mut writer, e.ip_numbers[0]).unwrap_err().content().unwrap(),
+                        &HopByHopNotAtStart
+                    );
+                } else {
+                    // normal write
+                    {
+                        let mut writer = Vec::with_capacity(e.data.header_len());
+                        e.data.write(&mut writer, e.ip_numbers[0]).unwrap();
+
+                        if *e.ip_numbers.last().unwrap() != IPV6_HOP_BY_HOP {
+                            // decoding if there will be no duplicate hop by hop error
+                            // will be triggered
+                            let (read, read_next, _) = Ipv6Extensions::from_slice(
+                                e.ip_numbers[0],
+                                &writer
+                            ).unwrap();
+                            assert_eq!(e.data, read);
+                            assert_eq!(*e.ip_numbers.last().unwrap(), read_next);
+                        }
+                    }
+
+                    // write error
+                    {
+                        let mut buffer = Vec::with_capacity(e.data.header_len() - 1);
+                        buffer.resize(e.data.header_len() - 1, 0);
+                        let mut cursor = Cursor::new(&mut buffer[..]);
+
+                        let err = e.data.write(
+                            &mut cursor,
+                            e.ip_numbers[0]
+                        ).unwrap_err();
+
+                        assert!(err.io().is_some());
+                    }
+
+                    // missing reference (skip the last header)
+                    {
+                        use crate::err::ipv6_exts::ExtsWalkError::ExtNotReferenced;
+
+                        let mut missing_ref = e.clone();
+                        let missing_ext = missing_ref.introduce_missing_ref(post_header);
+
+                        let mut writer = Vec::with_capacity(e.data.header_len());
+                        let err = missing_ref.data.write(
+                            &mut writer,
+                            missing_ref.ip_numbers[0]
+                        ).unwrap_err();
+
+                        assert_eq!(
+                            err.content().unwrap(),
+                            &ExtNotReferenced{ missing_ext }
+                        );
+                    }
+                }
+            }
+
+            // test the parsing of different extension header combinations
+            for first_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                // single header parsing
+                run_test(
+                    &[*first_header, post_header],
+                    &[header_size],
+                    post_header,
+                );
+
+                for second_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                    // double header parsing
+                    run_test(
+                        &[*first_header, *second_header, post_header],
+                        &[header_size],
+                        post_header,
+                    );
+
+                    for third_header in &EXTENSION_KNOWN_IP_NUMBERS {
+                        // tripple header parsing
+                        run_test(
+                            &[*first_header, *second_header, *third_header, post_header],
+                            &[header_size],
+                            post_header,
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header_len(
+            hop_by_hop_options in ipv6_raw_ext_any(),
+            destination_options in ipv6_raw_ext_any(),
+            routing in ipv6_raw_ext_any(),
+            fragment in ipv6_fragment_any(),
+            auth in ip_auth_any(),
+            final_destination_options in ipv6_raw_ext_any(),
+        ) {
+            // None
+            {
+                let exts : Ipv6Extensions = Default::default();
+                assert_eq!(0, exts.header_len());
+            }
+
+            // All filled
+            {
+                let exts = Ipv6Extensions{
+                    hop_by_hop_options: Some(hop_by_hop_options.clone()),
+                    destination_options: Some(destination_options.clone()),
+                    routing: Some(
+                        Ipv6RoutingExtensions{
+                            routing: routing.clone(),
+                            final_destination_options: Some(final_destination_options.clone()),
+                        }
+                    ),
+                    fragment: Some(fragment.clone()),
+                    auth: Some(auth.clone()),
+                };
+                assert_eq!(
+                    exts.header_len(),
+                    (
+                        hop_by_hop_options.header_len() +
+                        destination_options.header_len() +
+                        routing.header_len() +
+                        final_destination_options.header_len() +
+                        fragment.header_len() +
+                        auth.header_len()
+                    )
+                );
+            }
+
+            // Routing without final destination options
+            {
+                let exts = Ipv6Extensions{
+                    hop_by_hop_options: Some(hop_by_hop_options.clone()),
+                    destination_options: Some(destination_options.clone()),
+                    routing: Some(
+                        Ipv6RoutingExtensions{
+                            routing: routing.clone(),
+                            final_destination_options: None,
+                        }
+                    ),
+                    fragment: Some(fragment.clone()),
+                    auth: Some(auth.clone()),
+                };
+                assert_eq!(
+                    exts.header_len(),
+                    (
+                        hop_by_hop_options.header_len() +
+                        destination_options.header_len() +
+                        routing.header_len() +
+                        fragment.header_len() +
+                        auth.header_len()
+                    )
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn set_next_headers(
+            hop_by_hop_options in ipv6_raw_ext_any(),
+            destination_options in ipv6_raw_ext_any(),
+            routing in ipv6_raw_ext_any(),
+            fragment in ipv6_fragment_any(),
+            auth in ip_auth_any(),
+            final_destination_options in ipv6_raw_ext_any(),
+            post_header in ip_number_any()
+                .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(),
+                    |v| !EXTENSION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x)
+                ),
+        ) {
+            // none filled
+            {
+                let mut exts : Ipv6Extensions = Default::default();
+                assert_eq!(post_header, exts.set_next_headers(post_header));
+                assert!(exts.hop_by_hop_options.is_none());
+                assert!(exts.destination_options.is_none());
+                assert!(exts.routing.is_none());
+                assert!(exts.fragment.is_none());
+                assert!(exts.auth.is_none());
+            }
+
+            // all filled
+            {
+                let mut exts = Ipv6Extensions{
+                    hop_by_hop_options: Some(hop_by_hop_options.clone()),
+                    destination_options: Some(destination_options.clone()),
+                    routing: Some(
+                        Ipv6RoutingExtensions{
+                            routing: routing.clone(),
+                            final_destination_options: Some(final_destination_options.clone()),
+                        }
+                    ),
+                    fragment: Some(fragment.clone()),
+                    auth: Some(auth.clone()),
+                };
+                assert_eq!(IPV6_HOP_BY_HOP, exts.set_next_headers(post_header));
+
+                assert_eq!(IPV6_DEST_OPTIONS, exts.hop_by_hop_options.as_ref().unwrap().next_header);
+                assert_eq!(IPV6_ROUTE, exts.destination_options.as_ref().unwrap().next_header);
+                assert_eq!(IPV6_FRAG, exts.routing.as_ref().unwrap().routing.next_header);
+                assert_eq!(AUTH, exts.fragment.as_ref().unwrap().next_header);
+                assert_eq!(IPV6_DEST_OPTIONS, exts.auth.as_ref().unwrap().next_header);
+                assert_eq!(post_header, exts.routing.as_ref().unwrap().final_destination_options.as_ref().unwrap().next_header);
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn next_header(
+            header_size in any::<u8>(),
+            post_header in ip_number_any()
+                .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(),
+                    |v| !EXTENSION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x)
+                ),)
+        {
+            // test empty
+            {
+                let exts : Ipv6Extensions = Default::default();
+                assert_eq!(post_header, exts.next_header(post_header).unwrap());
+            }
+
+            /// Run a test with the given ip numbers
+            fn run_test(ip_numbers: &[IpNumber], header_sizes: &[u8], post_header: IpNumber) {
+                // setup test header
+                let e = ExtensionTestHeaders::new(
+                    ip_numbers,
+                    header_sizes
+                );
+
+                if e.ip_numbers[1..e.ip_numbers.len()-1].iter().any(|&x| x == IPV6_HOP_BY_HOP) {
+                    // a hop by hop header that is not at the start triggers an error
+                    use crate::err::ipv6_exts::ExtsWalkError::HopByHopNotAtStart;
+                    assert_eq!(
+                        e.data.next_header(e.ip_numbers[0]).unwrap_err(),
+                        HopByHopNotAtStart
+                    );
+                } else {
+                    // normal header
+                    assert_eq!(
+                        *e.ip_numbers.last().unwrap(),
+                        e.data.next_header(e.ip_numbers[0]).unwrap()
+                    );
+
+                    // missing reference (skip the last header)
+                    {
+                        use crate::err::ipv6_exts::ExtsWalkError::ExtNotReferenced;
+
+                        let mut missing_ref = e.clone();
+                        let missing_ext = missing_ref.introduce_missing_ref(post_header);
+                        assert_eq!(
+                            missing_ref.data.next_header(missing_ref.ip_numbers[0]).unwrap_err(),
+                            ExtNotReferenced{ missing_ext }
+                        );
+                    }
+                }
+            }
+
+            // test the parsing of different extension header combinations
+            for first_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                // single header parsing
+                run_test(
+                    &[*first_header, post_header],
+                    &[header_size],
+                    post_header,
+                );
+
+                for second_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                    // double header parsing
+                    run_test(
+                        &[*first_header, *second_header, post_header],
+                        &[header_size],
+                        post_header,
+                    );
+
+                    for third_header in &EXTENSION_KNOWN_IP_NUMBERS {
+                        // tripple header parsing
+                        run_test(
+                            &[*first_header, *second_header, *third_header, post_header],
+                            &[header_size],
+                            post_header,
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn is_fragmenting_payload() {
+        // empty
+        assert_eq!(
+            false,
+            Ipv6Extensions {
+                hop_by_hop_options: None,
+                destination_options: None,
+                routing: None,
+                fragment: None,
+                auth: None,
+            }
+            .is_fragmenting_payload()
+        );
+
+        // non fragmenting frag header
+        assert_eq!(
+            false,
+            Ipv6Extensions {
+                hop_by_hop_options: None,
+                destination_options: None,
+                routing: None,
+                fragment: Some(Ipv6FragmentHeader::new(
+                    ip_number::UDP,
+                    IpFragOffset::ZERO,
+                    false,
+                    0
+                )),
+                auth: None,
+            }
+            .is_fragmenting_payload()
+        );
+
+        // fragmenting frag header
+        assert!(Ipv6Extensions {
+            hop_by_hop_options: None,
+            destination_options: None,
+            routing: None,
+            fragment: Some(Ipv6FragmentHeader::new(
+                ip_number::UDP,
+                IpFragOffset::ZERO,
+                true,
+                0
+            )),
+            auth: None,
+        }
+        .is_fragmenting_payload());
+    }
+
+    #[test]
+    fn is_empty() {
+        // empty
+        assert!(Ipv6Extensions {
+            hop_by_hop_options: None,
+            destination_options: None,
+            routing: None,
+            fragment: None,
+            auth: None,
+        }
+        .is_empty());
+
+        // hop_by_hop_options
+        assert_eq!(
+            false,
+            Ipv6Extensions {
+                hop_by_hop_options: Some(
+                    Ipv6RawExtHeader::new_raw(ip_number::UDP, &[1, 2, 3, 4, 5, 6]).unwrap()
+                ),
+                destination_options: None,
+                routing: None,
+                fragment: None,
+                auth: None,
+            }
+            .is_empty()
+        );
+
+        // destination_options
+        assert_eq!(
+            false,
+            Ipv6Extensions {
+                hop_by_hop_options: None,
+                destination_options: Some(
+                    Ipv6RawExtHeader::new_raw(ip_number::UDP, &[1, 2, 3, 4, 5, 6]).unwrap()
+                ),
+                routing: None,
+                fragment: None,
+                auth: None,
+            }
+            .is_empty()
+        );
+
+        // routing
+        assert_eq!(
+            false,
+            Ipv6Extensions {
+                hop_by_hop_options: None,
+                destination_options: None,
+                routing: Some(Ipv6RoutingExtensions {
+                    routing: Ipv6RawExtHeader::new_raw(ip_number::UDP, &[1, 2, 3, 4, 5, 6])
+                        .unwrap(),
+                    final_destination_options: None,
+                }),
+                fragment: None,
+                auth: None,
+            }
+            .is_empty()
+        );
+
+        // fragment
+        assert_eq!(
+            false,
+            Ipv6Extensions {
+                hop_by_hop_options: None,
+                destination_options: None,
+                routing: None,
+                fragment: Some(Ipv6FragmentHeader::new(
+                    ip_number::UDP,
+                    IpFragOffset::ZERO,
+                    true,
+                    0
+                )),
+                auth: None,
+            }
+            .is_empty()
+        );
+
+        // auth
+        assert_eq!(
+            false,
+            Ipv6Extensions {
+                hop_by_hop_options: None,
+                destination_options: None,
+                routing: None,
+                fragment: None,
+                auth: Some(IpAuthHeader::new(ip_number::UDP, 0, 0, &[]).unwrap()),
+            }
+            .is_empty()
+        );
+    }
+
+    #[test]
+    fn debug() {
+        use alloc::format;
+
+        let a: Ipv6Extensions = Default::default();
+        assert_eq!(
+            &format!(
+                "Ipv6Extensions {{ hop_by_hop_options: {:?}, destination_options: {:?}, routing: {:?}, fragment: {:?}, auth: {:?} }}",
+                a.hop_by_hop_options,
+                a.destination_options,
+                a.routing,
+                a.fragment,
+                a.auth,
+            ),
+            &format!("{:?}", a)
+        );
+    }
+
+    #[test]
+    fn clone_eq() {
+        let a: Ipv6Extensions = Default::default();
+        assert_eq!(a, a.clone());
+    }
+
+    #[test]
+    fn default() {
+        let a: Ipv6Extensions = Default::default();
+        assert_eq!(a.hop_by_hop_options, None);
+        assert_eq!(a.destination_options, None);
+        assert_eq!(a.routing, None);
+        assert_eq!(a.fragment, None);
+        assert_eq!(a.auth, None);
+    }
+}
diff --git a/src/net/ipv6_exts_slice.rs b/src/net/ipv6_exts_slice.rs
new file mode 100644
index 0000000..012cc7b
--- /dev/null
+++ b/src/net/ipv6_exts_slice.rs
@@ -0,0 +1,649 @@
+use crate::*;
+use core::slice::from_raw_parts;
+
+/// Slice containing the IPv6 extension headers present after the ip header.
+///
+/// Currently supported:
+/// * Authentication Header
+/// * Hop by Hop Options Header
+/// * Destination Options Header (before and after routing headers)
+/// * Routing Header
+/// * Fragment
+/// * Authentication Header
+///
+/// Currently not supported:
+/// * Encapsulating Security Payload Header (ESP)
+/// * Host Identity Protocol (HIP)
+/// * IP Mobility
+/// * Site Multihoming by IPv6 Intermediation (SHIM6)
+#[derive(Clone, Debug, Eq, PartialEq, Default)]
+pub struct Ipv6ExtensionsSlice<'a> {
+    /// IP protocol number of the first header present in the slice.
+    first_header: Option<IpNumber>,
+    /// True if a fragment header is present in the ipv6 header extensions that causes the payload to be fragmented.
+    fragmented: bool,
+    /// Slice containing ipv6 extension headers.
+    slice: &'a [u8],
+}
+
+impl<'a> Ipv6ExtensionsSlice<'a> {
+    /// Collects all ipv6 extension headers in a slice & checks if
+    /// a fragmentation header that fragments the packet is present.
+    pub fn from_slice(
+        start_ip_number: IpNumber,
+        start_slice: &'a [u8],
+    ) -> Result<(Ipv6ExtensionsSlice<'a>, IpNumber, &'a [u8]), err::ipv6_exts::HeaderSliceError>
+    {
+        let mut rest = start_slice;
+        let mut next_header = start_ip_number;
+        let mut fragmented = false;
+
+        use err::ipv6_exts::{HeaderError::*, HeaderSliceError::*};
+        use ip_number::*;
+
+        // the hop by hop header is required to occur directly after the ipv6 header
+        if IPV6_HOP_BY_HOP == next_header {
+            let slice = Ipv6RawExtHeaderSlice::from_slice(rest).map_err(Len)?;
+            rest = &rest[slice.slice().len()..];
+            next_header = slice.next_header();
+        }
+
+        loop {
+            match next_header {
+                IPV6_HOP_BY_HOP => {
+                    return Err(Content(HopByHopNotAtStart));
+                }
+                IPV6_DEST_OPTIONS | IPV6_ROUTE => {
+                    let slice = Ipv6RawExtHeaderSlice::from_slice(rest)
+                        .map_err(|err| Len(err.add_offset(start_slice.len() - rest.len())))?;
+                    // SAFETY:
+                    // Ipv6RawExtHeaderSlice::from_slice always generates
+                    // a subslice from the given slice rest. Therefor it is guaranteed
+                    // that len is always greater or equal the len of rest.
+                    rest = unsafe {
+                        let len = slice.slice().len();
+                        from_raw_parts(rest.as_ptr().add(len), rest.len() - len)
+                    };
+                    next_header = slice.next_header();
+                }
+                IPV6_FRAG => {
+                    let slice = Ipv6FragmentHeaderSlice::from_slice(rest)
+                        .map_err(|err| Len(err.add_offset(start_slice.len() - rest.len())))?;
+                    // SAFETY:
+                    // Ipv6FragmentHeaderSlice::from_slice always generates
+                    // a subslice from the given slice rest. Therefor it is guaranteed
+                    // that len is always greater or equal the len of rest.
+                    rest = unsafe {
+                        let len = slice.slice().len();
+                        from_raw_parts(rest.as_ptr().add(len), rest.len() - len)
+                    };
+                    next_header = slice.next_header();
+
+                    // check if the fragment header actually causes fragmentation
+                    fragmented = fragmented || slice.is_fragmenting_payload();
+                }
+                AUTH => {
+                    let slice = IpAuthHeaderSlice::from_slice(rest).map_err(|err| {
+                        use err::ip_auth::HeaderSliceError as I;
+                        match err {
+                            I::Len(err) => Len(err.add_offset(start_slice.len() - rest.len())),
+                            I::Content(err) => Content(IpAuth(err)),
+                        }
+                    })?;
+                    // SAFETY:
+                    // IpAuthHeaderSlice::from_slice always generates
+                    // a subslice from the given slice rest. Therefor it is guaranteed
+                    // that len is always greater or equal the len of rest.
+                    rest = unsafe {
+                        let len = slice.slice().len();
+                        from_raw_parts(rest.as_ptr().add(len), rest.len() - len)
+                    };
+                    next_header = slice.next_header();
+                }
+                // done parsing, the next header is not a known/supported header extension
+                _ => break,
+            }
+        }
+
+        Ok((
+            Ipv6ExtensionsSlice {
+                first_header: if rest.len() != start_slice.len() {
+                    Some(start_ip_number)
+                } else {
+                    None
+                },
+                fragmented,
+                slice: &start_slice[..start_slice.len() - rest.len()],
+            },
+            next_header,
+            rest,
+        ))
+    }
+
+    /// Collects all ipv6 extension headers in a slice until an error
+    /// is encountered or a "non IP extension header" is found and
+    /// returns the successfully parsed parts (+ the unparsed slice
+    /// it's `IpNumber` and the error if one occurred).
+    ///
+    /// The returned values are
+    ///
+    /// * [`Ipv6ExtensionsSlice`] containing the successfully parsed IPv6 extension headers
+    /// * [`IpNumber`] of unparsed data
+    /// * Slice with unparsed data
+    /// * Optional with error if there was an error wich stoped the parsing.
+    pub fn from_slice_lax(
+        start_ip_number: IpNumber,
+        start_slice: &'a [u8],
+    ) -> (
+        Ipv6ExtensionsSlice<'a>,
+        IpNumber,
+        &'a [u8],
+        Option<(err::ipv6_exts::HeaderSliceError, err::Layer)>,
+    ) {
+        let mut rest = start_slice;
+        let mut next_header = start_ip_number;
+        let mut error = None;
+        let mut fragmented = false;
+
+        use err::ipv6_exts::{HeaderError::*, HeaderSliceError::*};
+        use ip_number::*;
+
+        // the hop by hop header is required to occur directly after the ipv6 header
+        if IPV6_HOP_BY_HOP == next_header {
+            match Ipv6RawExtHeaderSlice::from_slice(rest) {
+                Ok(slice) => {
+                    rest = &rest[slice.slice().len()..];
+                    next_header = slice.next_header();
+                }
+                Err(err) => {
+                    error = Some((Len(err), err::Layer::Ipv6HopByHopHeader));
+                }
+            }
+        }
+
+        while error.is_none() {
+            match next_header {
+                IPV6_HOP_BY_HOP => {
+                    error = Some((Content(HopByHopNotAtStart), err::Layer::Ipv6HopByHopHeader));
+                    break;
+                }
+                IPV6_DEST_OPTIONS | IPV6_ROUTE => {
+                    let slice = match Ipv6RawExtHeaderSlice::from_slice(rest) {
+                        Ok(s) => s,
+                        Err(err) => {
+                            error = Some((
+                                Len(err.add_offset(start_slice.len() - rest.len())),
+                                if next_header == IPV6_DEST_OPTIONS {
+                                    err::Layer::Ipv6DestOptionsHeader
+                                } else {
+                                    err::Layer::Ipv6RouteHeader
+                                },
+                            ));
+                            break;
+                        }
+                    };
+                    // SAFETY:
+                    // Ipv6RawExtHeaderSlice::from_slice always generates
+                    // a subslice from the given slice rest. Therefor it is guranteed
+                    // that len is always greater or equal the len of rest.
+                    rest = unsafe {
+                        let len = slice.slice().len();
+                        from_raw_parts(rest.as_ptr().add(len), rest.len() - len)
+                    };
+                    next_header = slice.next_header();
+                }
+                IPV6_FRAG => {
+                    let slice = match Ipv6FragmentHeaderSlice::from_slice(rest) {
+                        Ok(s) => s,
+                        Err(err) => {
+                            error = Some((
+                                Len(err.add_offset(start_slice.len() - rest.len())),
+                                err::Layer::Ipv6FragHeader,
+                            ));
+                            break;
+                        }
+                    };
+
+                    // SAFETY:
+                    // Ipv6FragmentHeaderSlice::from_slice always generates
+                    // a subslice from the given slice rest. Therefor it is guranteed
+                    // that len is always greater or equal the len of rest.
+                    rest = unsafe {
+                        let len = slice.slice().len();
+                        from_raw_parts(rest.as_ptr().add(len), rest.len() - len)
+                    };
+                    next_header = slice.next_header();
+
+                    // check if the fragment header actually causes fragmentation
+                    fragmented = fragmented || slice.is_fragmenting_payload();
+                }
+                AUTH => {
+                    use err::ip_auth::HeaderSliceError as I;
+                    let slice = match IpAuthHeaderSlice::from_slice(rest) {
+                        Ok(s) => s,
+                        Err(err) => {
+                            error = Some((
+                                match err {
+                                    I::Len(err) => {
+                                        Len(err.add_offset(start_slice.len() - rest.len()))
+                                    }
+                                    I::Content(err) => Content(IpAuth(err)),
+                                },
+                                err::Layer::IpAuthHeader,
+                            ));
+                            break;
+                        }
+                    };
+                    // SAFETY:
+                    // IpAuthHeaderSlice::from_slice always generates
+                    // a subslice from the given slice rest. Therefor it is guranteed
+                    // that len is always greater or equal the len of rest.
+                    rest = unsafe {
+                        let len = slice.slice().len();
+                        from_raw_parts(rest.as_ptr().add(len), rest.len() - len)
+                    };
+                    next_header = slice.next_header();
+                }
+                // done parsing, the next header is not a known/supported header extension
+                _ => break,
+            }
+        }
+
+        (
+            Ipv6ExtensionsSlice {
+                first_header: if rest.len() != start_slice.len() {
+                    Some(start_ip_number)
+                } else {
+                    None
+                },
+                fragmented,
+                slice: &start_slice[..start_slice.len() - rest.len()],
+            },
+            next_header,
+            rest,
+            error,
+        )
+    }
+
+    /// Returns true if a fragmentation header is present in
+    /// the extensions that fragments the payload.
+    ///
+    /// Note: A fragmentation header can still be present
+    /// even if the return value is false in case the fragmentation
+    /// headers don't fragment the payload. This is the case if
+    /// the offset of all fragmentation header is 0 and the
+    /// more fragment bit is not set.
+    #[inline]
+    pub fn is_fragmenting_payload(&self) -> bool {
+        self.fragmented
+    }
+
+    /// Returns the ip protocol number of the first header in the slice
+    /// if the slice contains an ipv6 extension header. If no ipv6 header
+    /// is present None is returned.
+    ///
+    /// None is only returned if the slice length of this struct is 0.
+    #[inline]
+    pub fn first_header(&self) -> Option<IpNumber> {
+        self.first_header
+    }
+
+    /// Slice containing the ipv6 extension headers.
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+
+    /// Returns true if no IPv6 extension header is present (slice is empty).
+    #[inline]
+    pub fn is_empty(&self) -> bool {
+        self.slice.is_empty()
+    }
+}
+
+impl<'a> IntoIterator for Ipv6ExtensionsSlice<'a> {
+    type Item = Ipv6ExtensionSlice<'a>;
+    type IntoIter = Ipv6ExtensionSliceIter<'a>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        Ipv6ExtensionSliceIter {
+            // map the next header None value to some non ipv6 ext header
+            // value.
+            next_header: self.first_header.unwrap_or(ip_number::UDP),
+            rest: self.slice,
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::ipv6_exts_test_helpers::*;
+    use super::*;
+    use crate::ip_number::*;
+    use crate::test_gens::*;
+    use alloc::{borrow::ToOwned, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            header_size in any::<u8>(),
+            post_header in ip_number_any()
+                .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(),
+                    |v| !EXTENSION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x)
+                )
+        ) {
+            use err::ipv6_exts::{HeaderError::*, HeaderSliceError::*};
+
+            // no extension headers filled
+            {
+                let some_data = [1,2,3,4];
+                let actual = Ipv6ExtensionsSlice::from_slice(UDP, &some_data).unwrap();
+                assert_eq!(actual.0.is_fragmenting_payload(), false);
+                assert_eq!(actual.0.first_header(), None);
+                assert_eq!(actual.0.slice().len(), 0);
+                assert_eq!(actual.1, UDP);
+                assert_eq!(actual.2, &some_data);
+            }
+
+            /// Run a test with the given ip numbers
+            fn run_test(ip_numbers: &[IpNumber], header_sizes: &[u8]) {
+                // setup test payload
+                let e = ExtensionTestPayload::new(
+                    ip_numbers,
+                    header_sizes
+                );
+
+                if e.ip_numbers[1..].iter().any(|&x| x == IPV6_HOP_BY_HOP) {
+                    // a hop by hop header that is not at the start triggers an error
+                    assert_eq!(
+                        Ipv6ExtensionsSlice::from_slice(ip_numbers[0], e.slice()).unwrap_err(),
+                        Content(HopByHopNotAtStart)
+                    );
+                } else {
+                    // normal read
+                    let (header, next, rest) = Ipv6ExtensionsSlice::from_slice(ip_numbers[0], e.slice()).unwrap();
+                    assert_eq!(header.first_header(), Some(ip_numbers[0]));
+                    assert_eq!(header.slice(), e.slice());
+                    assert_eq!(next, *ip_numbers.last().unwrap());
+                    assert_eq!(rest, &e.slice()[e.slice().len()..]);
+
+                    // unexpected end of slice
+                    {
+                        let offset: usize = e.lengths[..e.lengths.len() - 1].into_iter().sum();
+
+                        assert_eq!(
+                            Ipv6ExtensionsSlice::from_slice(ip_numbers[0], &e.slice()[..e.slice().len() - 1]).unwrap_err(),
+                            Len(err::LenError {
+                                required_len: e.slice().len() - offset,
+                                len: e.slice().len() - offset - 1,
+                                len_source: LenSource::Slice,
+                                layer: match ip_numbers[ip_numbers.len() - 2] {
+                                    AUTH => err::Layer::IpAuthHeader,
+                                    IPV6_FRAG => err::Layer::Ipv6FragHeader,
+                                    _ => err::Layer::Ipv6ExtHeader
+                                },
+                                layer_start_offset: offset,
+                            })
+                        );
+                    }
+                }
+            }
+
+            // test the parsing of different extension header combinations
+            for first_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                // single header parsing
+                run_test(
+                    &[*first_header, post_header],
+                    &[header_size],
+                );
+
+                for second_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                    // double header parsing
+                    run_test(
+                        &[*first_header, *second_header, post_header],
+                        &[header_size],
+                    );
+
+                    for third_header in &EXTENSION_KNOWN_IP_NUMBERS {
+                        // tripple header parsing
+                        run_test(
+                            &[*first_header, *second_header, *third_header, post_header],
+                            &[header_size],
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice_lax(
+            header_size in any::<u8>(),
+            post_header in ip_number_any()
+                .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(),
+                    |v| !EXTENSION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x)
+                )
+        ) {
+            use err::ipv6_exts::{HeaderError::*, HeaderSliceError::*};
+
+            // no extension headers filled
+            {
+                let some_data = [1,2,3,4];
+                let actual = Ipv6ExtensionsSlice::from_slice_lax(UDP, &some_data);
+                assert_eq!(actual.0.is_fragmenting_payload(), false);
+                assert_eq!(actual.0.first_header(), None);
+                assert_eq!(actual.0.slice().len(), 0);
+                assert_eq!(actual.1, UDP);
+                assert_eq!(actual.2, &some_data);
+            }
+
+            /// Run a test with the given ip numbers
+            fn run_test(ip_numbers: &[IpNumber], header_sizes: &[u8]) {
+                // setup test payload
+                let e = ExtensionTestPayload::new(
+                    ip_numbers,
+                    header_sizes
+                );
+
+                if e.ip_numbers[1..].iter().any(|&x| x == IPV6_HOP_BY_HOP) {
+                    // a hop by hop header that is not at the start triggers an error
+                    assert_eq!(
+                        Ipv6ExtensionsSlice::from_slice_lax(ip_numbers[0], e.slice()).3.unwrap(),
+                        (Content(HopByHopNotAtStart), err::Layer::Ipv6HopByHopHeader)
+                    );
+                } else {
+                    // normal read
+                    let actual_normal = Ipv6ExtensionsSlice::from_slice_lax(ip_numbers[0], e.slice());
+                    assert_eq!(actual_normal.0.first_header(), Some(ip_numbers[0]));
+                    assert_eq!(actual_normal.0.slice(), e.slice());
+                    assert_eq!(actual_normal.1, *ip_numbers.last().unwrap());
+                    assert_eq!(actual_normal.2, &[]);
+
+                    // unexpected end of slice
+                    {
+                        let offset: usize = e.lengths[..e.lengths.len() - 1].into_iter().sum();
+
+                        let actual = Ipv6ExtensionsSlice::from_slice_lax(
+                            ip_numbers[0],
+                            &e.slice()[..e.slice().len() - 1]
+                        );
+                        assert_eq!(&e.slice()[offset..e.slice().len() - 1], actual.2);
+                        assert_eq!(
+                            actual.3.unwrap().0,
+                            Len(err::LenError {
+                                required_len: e.slice().len() - offset,
+                                len: e.slice().len() - offset - 1,
+                                len_source: LenSource::Slice,
+                                layer: match ip_numbers[ip_numbers.len() - 2] {
+                                    AUTH => err::Layer::IpAuthHeader,
+                                    IPV6_FRAG => err::Layer::Ipv6FragHeader,
+                                    _ => err::Layer::Ipv6ExtHeader
+                                },
+                                layer_start_offset: offset,
+                            })
+                        );
+                    }
+                }
+            }
+
+            // test the parsing of different extension header combinations
+            for first_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                // single header parsing
+                run_test(
+                    &[*first_header, post_header],
+                    &[header_size],
+                );
+
+                for second_header in &EXTENSION_KNOWN_IP_NUMBERS {
+
+                    // double header parsing
+                    run_test(
+                        &[*first_header, *second_header, post_header],
+                        &[header_size],
+                    );
+
+                    for third_header in &EXTENSION_KNOWN_IP_NUMBERS {
+                        // tripple header parsing
+                        run_test(
+                            &[*first_header, *second_header, *third_header, post_header],
+                            &[header_size],
+                        );
+                    }
+                }
+            }
+
+            // test that the auth content error gets forwarded
+            {
+                let auth = IpAuthHeader::new(post_header, 0, 0, &[]).unwrap();
+                let mut bytes = auth.to_bytes();
+                // inject an invalid len value
+                bytes[1] = 0;
+                let actual = Ipv6ExtensionsSlice::from_slice_lax(AUTH, &bytes);
+
+                use err::ipv6_exts::HeaderError::IpAuth;
+                use err::ip_auth::HeaderError::ZeroPayloadLen;
+                assert_eq!(actual.0.slice(), &[]);
+                assert_eq!(actual.1, AUTH);
+                assert_eq!(actual.2, &bytes[..]);
+                assert_eq!(actual.3.unwrap().0.content().unwrap(), &IpAuth(ZeroPayloadLen));
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn is_fragmenting_payload(
+            hop_by_hop_options in ipv6_raw_ext_any(),
+            destination_options in ipv6_raw_ext_any(),
+            routing in ipv6_raw_ext_any(),
+            auth in ip_auth_any(),
+            final_destination_options in ipv6_raw_ext_any()
+        ) {
+            // no fragment header
+            {
+                let mut exts = Ipv6Extensions{
+                    hop_by_hop_options: Some(hop_by_hop_options),
+                    destination_options: Some(destination_options),
+                    routing: Some(
+                        Ipv6RoutingExtensions {
+                            routing,
+                            final_destination_options: Some(final_destination_options),
+                        }
+                    ),
+                    fragment: None,
+                    auth: Some(auth),
+                };
+                let first_ip_number = exts.set_next_headers(UDP);
+
+                let mut bytes = Vec::with_capacity(exts.header_len());
+                exts.write(&mut bytes, first_ip_number).unwrap();
+
+                let (header, _, _) = Ipv6ExtensionsSlice::from_slice(first_ip_number, &bytes).unwrap();
+                assert_eq!(false, header.is_fragmenting_payload());
+            }
+
+            // different variants of the fragment header with
+            // variants that fragment and variants that don't fragment
+            let frag_variants : [(bool, Ipv6FragmentHeader);4] = [
+                (false, Ipv6FragmentHeader::new(UDP, 0.try_into().unwrap(), false, 123)),
+                (true, Ipv6FragmentHeader::new(UDP, 2.try_into().unwrap(), false, 123)),
+                (true, Ipv6FragmentHeader::new(UDP, 0.try_into().unwrap(), true, 123)),
+                (true, Ipv6FragmentHeader::new(UDP, 3.try_into().unwrap(), true, 123)),
+            ];
+
+            for (first_expected, first_header) in frag_variants.iter() {
+                // single fragment header
+                {
+                    let bytes = first_header.to_bytes();
+                    let (header, _, _) = Ipv6ExtensionsSlice::from_slice(IPV6_FRAG, &bytes).unwrap();
+                    assert_eq!(*first_expected, header.is_fragmenting_payload());
+                }
+                // two fragment headers
+                for (second_expected, second_header) in frag_variants.iter() {
+                    let mut first_mod = first_header.clone();
+                    first_mod.next_header = IPV6_FRAG;
+                    let mut bytes = Vec::with_capacity(first_mod.header_len() + second_header.header_len());
+                    bytes.extend_from_slice(&first_mod.to_bytes());
+                    bytes.extend_from_slice(&second_header.to_bytes());
+
+                    let (header, _, _) = Ipv6ExtensionsSlice::from_slice(IPV6_FRAG, &bytes).unwrap();
+                    assert_eq!(
+                        *first_expected || *second_expected,
+                        header.is_fragmenting_payload()
+                    );
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn is_empty() {
+        // empty
+        {
+            let slice = Ipv6ExtensionsSlice::from_slice(ip_number::UDP, &[])
+                .unwrap()
+                .0;
+            assert!(slice.is_empty());
+        }
+
+        // fragment
+        {
+            let bytes =
+                Ipv6FragmentHeader::new(ip_number::UDP, IpFragOffset::ZERO, true, 0).to_bytes();
+            let slice = Ipv6ExtensionsSlice::from_slice(ip_number::IPV6_FRAG, &bytes)
+                .unwrap()
+                .0;
+            assert_eq!(false, slice.is_empty());
+        }
+    }
+
+    #[test]
+    fn debug() {
+        use alloc::format;
+
+        let a: Ipv6ExtensionsSlice = Default::default();
+        assert_eq!(
+            "Ipv6ExtensionsSlice { first_header: None, fragmented: false, slice: [] }",
+            &format!("{:?}", a)
+        );
+    }
+
+    #[test]
+    fn clone_eq() {
+        let a: Ipv6ExtensionsSlice = Default::default();
+        assert_eq!(a, a.clone());
+    }
+
+    #[test]
+    fn default() {
+        let a: Ipv6ExtensionsSlice = Default::default();
+        assert_eq!(a.is_fragmenting_payload(), false);
+        assert_eq!(a.first_header(), None);
+        assert_eq!(a.slice().len(), 0);
+    }
+}
diff --git a/src/net/ipv6_flow_label.rs b/src/net/ipv6_flow_label.rs
new file mode 100644
index 0000000..7b95617
--- /dev/null
+++ b/src/net/ipv6_flow_label.rs
@@ -0,0 +1,298 @@
+use crate::err::ValueTooBigError;
+
+/// The IPv6 "Flow Label" is a 20 bit unsigned integer present in
+/// the [`crate::Ipv6Header`].
+///
+/// # Example Usage:
+///
+/// ```
+/// use etherparse::Ipv6FlowLabel;
+///
+/// // try into
+/// {
+///     let flow_label: Ipv6FlowLabel = 123.try_into().unwrap();
+///     assert_eq!(flow_label.value(), 123);
+///
+///     // fragment offset can always be converted back to an u32
+///     let value: u32 = flow_label.into();
+///     assert_eq!(123, value);
+/// }
+///
+/// // via try_new
+/// {
+///     let flow_label = Ipv6FlowLabel::try_new(123).unwrap();
+///     assert_eq!(flow_label.value(), 123);
+///
+///     // note that only 20 bit numbers are allowed (meaning
+///     // 0b1111_11111111_11111111 is the maximum allowed value)
+///     use etherparse::err::{ValueTooBigError, ValueType};
+///     assert_eq!(
+///         Ipv6FlowLabel::try_new(Ipv6FlowLabel::MAX_U32 + 1),
+///         Err(ValueTooBigError{
+///             actual: Ipv6FlowLabel::MAX_U32 + 1,
+///             max_allowed: Ipv6FlowLabel::MAX_U32,
+///             value_type: ValueType::Ipv6FlowLabel,
+///         })
+///     );
+/// }
+///
+/// // via new_unchecked
+/// {
+///     // in case you are sure the number does not exceed the max
+///     // you can use the unsafe new_unchecked function
+///     let flow_label = unsafe {
+///         // please make sure that the value is not greater than Ipv6FlowLabel::MAX_U32
+///         // before calling this method
+///         Ipv6FlowLabel::new_unchecked(123)
+///     };
+///     assert_eq!(flow_label.value(), 123);
+/// }
+/// ```
+#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct Ipv6FlowLabel(u32);
+
+impl Ipv6FlowLabel {
+    /// Ipv6FlowLabel with value 0.
+    pub const ZERO: Ipv6FlowLabel = Ipv6FlowLabel(0);
+
+    /// Maximum value of an IPv6 Flow Label.
+    pub const MAX_U32: u32 = 0b1111_1111_1111_1111_1111;
+
+    /// Tries to create an [`Ipv6FlowLabel`] and checks that the passed value
+    /// is smaller or equal than [`Ipv6FlowLabel::MAX_U32`] (20 bit unsigned integer).
+    ///
+    /// In case the passed value is bigger then what can be represented in an 20 bit
+    /// integer an error is returned. Otherwise an `Ok` containing the [`Ipv6FlowLabel`].
+    ///
+    /// ```
+    /// use etherparse::Ipv6FlowLabel;
+    ///
+    /// let frag_offset = Ipv6FlowLabel::try_new(123).unwrap();
+    /// assert_eq!(frag_offset.value(), 123);
+    ///
+    /// // if a number that can not be represented in an 20 bit integer
+    /// // gets passed in an error is returned
+    /// use etherparse::err::{ValueTooBigError, ValueType};
+    /// assert_eq!(
+    ///     Ipv6FlowLabel::try_new(Ipv6FlowLabel::MAX_U32 + 1),
+    ///     Err(ValueTooBigError{
+    ///         actual: Ipv6FlowLabel::MAX_U32 + 1,
+    ///         max_allowed: Ipv6FlowLabel::MAX_U32,
+    ///         value_type: ValueType::Ipv6FlowLabel,
+    ///     })
+    /// );
+    /// ```
+    #[inline]
+    pub const fn try_new(value: u32) -> Result<Ipv6FlowLabel, ValueTooBigError<u32>> {
+        use crate::err::ValueType;
+        if value <= Ipv6FlowLabel::MAX_U32 {
+            Ok(Ipv6FlowLabel(value))
+        } else {
+            Err(ValueTooBigError {
+                actual: value,
+                max_allowed: Ipv6FlowLabel::MAX_U32,
+                value_type: ValueType::Ipv6FlowLabel,
+            })
+        }
+    }
+
+    /// Creates an [`Ipv6FlowLabel`] without checking that the value
+    /// is smaller or equal than [`Ipv6FlowLabel::MAX_U32`] (20 bit unsigned integer).
+    /// The caller must guarantee that `value <= Ipv6FlowLabel::MAX_U32`.
+    ///
+    /// # Safety
+    ///
+    /// `value` must be smaller or equal than [`Ipv6FlowLabel::MAX_U32`]
+    /// otherwise the behavior of functions or data structures relying
+    /// on this pre-requirement is undefined.
+    #[inline]
+    pub const unsafe fn new_unchecked(value: u32) -> Ipv6FlowLabel {
+        debug_assert!(value <= Ipv6FlowLabel::MAX_U32);
+        Ipv6FlowLabel(value)
+    }
+
+    /// Returns the underlying unsigned 20 bit value as an `u32` value.
+    #[inline]
+    pub const fn value(self) -> u32 {
+        self.0
+    }
+}
+
+impl core::fmt::Display for Ipv6FlowLabel {
+    #[inline]
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+impl From<Ipv6FlowLabel> for u32 {
+    #[inline]
+    fn from(value: Ipv6FlowLabel) -> Self {
+        value.0
+    }
+}
+
+impl TryFrom<u32> for Ipv6FlowLabel {
+    type Error = ValueTooBigError<u32>;
+
+    #[inline]
+    fn try_from(value: u32) -> Result<Self, Self::Error> {
+        use crate::err::ValueType;
+        if value <= Ipv6FlowLabel::MAX_U32 {
+            Ok(Ipv6FlowLabel(value))
+        } else {
+            Err(Self::Error {
+                actual: value,
+                max_allowed: Ipv6FlowLabel::MAX_U32,
+                value_type: ValueType::Ipv6FlowLabel,
+            })
+        }
+    }
+}
+
+#[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 = Ipv6FlowLabel(123);
+            let b = a;
+            assert_eq!(a, b);
+            assert_eq!(a.clone(), a);
+        }
+
+        // default
+        {
+            let actual: Ipv6FlowLabel = Default::default();
+            assert_eq!(actual.value(), 0);
+        }
+
+        // debug
+        {
+            let a = Ipv6FlowLabel(123);
+            assert_eq!(format!("{:?}", a), format!("Ipv6FlowLabel(123)"));
+        }
+
+        // ord & partial ord
+        {
+            use core::cmp::Ordering;
+            let a = Ipv6FlowLabel(123);
+            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();
+                Ipv6FlowLabel(123).hash(&mut hasher);
+                hasher.finish()
+            };
+            let b = {
+                let mut hasher = DefaultHasher::new();
+                Ipv6FlowLabel(123).hash(&mut hasher);
+                hasher.finish()
+            };
+            assert_eq!(a, b);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn try_new(
+            valid_value in 0..=0b1111_11111111_11111111u32,
+            invalid_value in 0b1_0000_00000000_00000000u32..=u32::MAX
+        ) {
+            use crate::err::{ValueType, ValueTooBigError};
+            assert_eq!(
+                valid_value,
+                Ipv6FlowLabel::try_new(valid_value).unwrap().value()
+            );
+            assert_eq!(
+                Ipv6FlowLabel::try_new(invalid_value).unwrap_err(),
+                ValueTooBigError{
+                    actual: invalid_value,
+                    max_allowed: 0b1111_11111111_11111111,
+                    value_type:  ValueType::Ipv6FlowLabel
+                }
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn try_from(
+            valid_value in 0..=0b1111_11111111_11111111u32,
+            invalid_value in 0b1_0000_00000000_00000000u32..=u32::MAX
+        ) {
+            use crate::err::{ValueType, ValueTooBigError};
+            // try_into
+            {
+                let actual: Ipv6FlowLabel = valid_value.try_into().unwrap();
+                assert_eq!(actual.value(), valid_value);
+
+                let err: Result<Ipv6FlowLabel, ValueTooBigError<u32>> = invalid_value.try_into();
+                assert_eq!(
+                    err.unwrap_err(),
+                    ValueTooBigError{
+                        actual: invalid_value,
+                        max_allowed: 0b1111_11111111_11111111,
+                        value_type:  ValueType::Ipv6FlowLabel
+                    }
+                );
+            }
+            // try_from
+            {
+                assert_eq!(
+                    Ipv6FlowLabel::try_from(valid_value).unwrap().value(),
+                    valid_value
+                );
+
+                assert_eq!(
+                    Ipv6FlowLabel::try_from(invalid_value).unwrap_err(),
+                    ValueTooBigError{
+                        actual: invalid_value,
+                        max_allowed: 0b1111_11111111_11111111,
+                        value_type:  ValueType::Ipv6FlowLabel
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn new_unchecked(valid_value in 0..=0b1111_11111111_11111111u32) {
+            assert_eq!(
+                valid_value,
+                unsafe {
+                    Ipv6FlowLabel::new_unchecked(valid_value).value()
+                }
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn fmt(valid_value in 0..=0b1111_11111111_11111111u32) {
+            assert_eq!(format!("{}", Ipv6FlowLabel(valid_value)), format!("{}", valid_value));
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from(valid_value in 0..=0b1111_11111111_11111111u32,) {
+            let frag_offset = Ipv6FlowLabel::try_new(valid_value).unwrap();
+            let actual: u32 = frag_offset.into();
+            assert_eq!(actual, valid_value);
+        }
+    }
+}
diff --git a/src/net/ipv6_fragment_header.rs b/src/net/ipv6_fragment_header.rs
new file mode 100644
index 0000000..4ab7486
--- /dev/null
+++ b/src/net/ipv6_fragment_header.rs
@@ -0,0 +1,428 @@
+use super::super::*;
+
+/// IPv6 fragment header.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Ipv6FragmentHeader {
+    /// IP protocol number specifying the next header or transport layer protocol.
+    ///
+    /// See [IpNumber] or [ip_number] for a definition of the known values.
+    pub next_header: IpNumber,
+    /// Offset of the current IP payload relative to the start of the fragmented
+    /// packet payload.
+    pub fragment_offset: IpFragOffset,
+    /// True if more fragment packets will follow. False if this is the last packet.
+    pub more_fragments: bool,
+    /// Identifcation value generated by the source.
+    pub identification: u32,
+}
+
+impl Ipv6FragmentHeader {
+    /// Length of the serialized header.
+    pub const LEN: usize = 8;
+
+    /// Create a new fragmentation header with the given parameters.
+    ///
+    /// Note that the `fragment_offset` can only support values between 0 and 0x1fff (inclusive).
+    pub const fn new(
+        next_header: IpNumber,
+        fragment_offset: IpFragOffset,
+        more_fragments: bool,
+        identification: u32,
+    ) -> Ipv6FragmentHeader {
+        Ipv6FragmentHeader {
+            next_header,
+            fragment_offset,
+            more_fragments,
+            identification,
+        }
+    }
+
+    /// Read an Ipv6FragmentHeader from a slice and return the header & unused parts of the slice.
+    pub fn from_slice(slice: &[u8]) -> Result<(Ipv6FragmentHeader, &[u8]), err::LenError> {
+        let s = Ipv6FragmentHeaderSlice::from_slice(slice)?;
+        let rest = &slice[8..];
+        let header = s.to_header();
+        Ok((header, rest))
+    }
+
+    /// Read an fragment header from the current reader position.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut T,
+    ) -> Result<Ipv6FragmentHeader, std::io::Error> {
+        let buffer = {
+            let mut buffer: [u8; 8] = [0; 8];
+            reader.read_exact(&mut buffer)?;
+            buffer
+        };
+
+        Ok(Ipv6FragmentHeader {
+            next_header: IpNumber(buffer[0]),
+            fragment_offset: unsafe {
+                // SAFE as the resulting number is guaranteed to have at most
+                // 13 bits.
+                IpFragOffset::new_unchecked(u16::from_be_bytes([
+                    (buffer[2] >> 3) & 0b0001_1111u8,
+                    ((buffer[2] << 5) & 0b1110_0000u8) | (buffer[3] & 0b0001_1111u8),
+                ]))
+            },
+            more_fragments: 0 != buffer[3] & 0b1000_0000u8,
+            identification: u32::from_be_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]),
+        })
+    }
+
+    /// Read an fragment header from the current reader position.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read_limited<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut crate::io::LimitedReader<T>,
+    ) -> Result<Ipv6FragmentHeader, crate::err::io::LimitedReadError> {
+        use err::Layer;
+
+        // set layer so errors contain the correct layer & offset
+        reader.start_layer(Layer::Ipv6FragHeader);
+
+        let buffer = {
+            let mut buffer: [u8; 8] = [0; 8];
+            reader.read_exact(&mut buffer)?;
+            buffer
+        };
+
+        Ok(Ipv6FragmentHeader {
+            next_header: IpNumber(buffer[0]),
+            fragment_offset: unsafe {
+                // SAFE as the resulting number is guaranteed to have at most
+                // 13 bits.
+                IpFragOffset::new_unchecked(u16::from_be_bytes([
+                    (buffer[2] >> 3) & 0b0001_1111u8,
+                    ((buffer[2] << 5) & 0b1110_0000u8) | (buffer[3] & 0b0001_1111u8),
+                ]))
+            },
+            more_fragments: 0 != buffer[3] & 0b1000_0000u8,
+            identification: u32::from_be_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]),
+        })
+    }
+
+    /// Writes a given IPv6 fragment header to the current position.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
+        writer.write_all(&self.to_bytes())
+    }
+
+    /// Length of the header in bytes.
+    #[inline]
+    pub fn header_len(&self) -> usize {
+        Ipv6FragmentHeader::LEN
+    }
+
+    /// Checks if the fragment header actually fragments the packet.
+    ///
+    /// Returns false if the fragment offset is 0 and the more flag
+    /// is not set. Otherwise returns true.
+    ///
+    /// [RFC8200](https://datatracker.ietf.org/doc/html/rfc8200) explicitly
+    /// states that fragment headers that don't fragment the packet payload are
+    /// allowed. See the following quote from
+    /// RFC8200 page 32:
+    ///
+    /// > Revised the text to handle the case of fragments that are whole
+    /// > datagrams (i.e., both the Fragment Offset field and the M flag
+    /// > are zero).  If received, they should be processed as a
+    /// > reassembled packet.  Any other fragments that match should be
+    /// > processed independently.  The Fragment creation process was
+    /// > modified to not create whole datagram fragments (Fragment
+    /// > Offset field and the M flag are zero).  See
+    /// > [RFC6946](https://datatracker.ietf.org/doc/html/6946) and
+    /// > [RFC8021](https://datatracker.ietf.org/doc/html/rfc8021) for more
+    /// > information."
+    ///
+    /// ```
+    /// use etherparse::{Ipv6FragmentHeader, ip_number::UDP};
+    ///
+    /// // offset 0 & no more fragments result in an unfragmented payload
+    /// {
+    ///     let header = Ipv6FragmentHeader::new(UDP, 0.try_into().unwrap(), false, 123);
+    ///     assert!(false == header.is_fragmenting_payload());
+    /// }
+    ///
+    /// // offset 0 & but more fragments will come -> fragmented
+    /// {
+    ///     let header = Ipv6FragmentHeader::new(UDP, 0.try_into().unwrap(), true, 123);
+    ///     assert!(header.is_fragmenting_payload());
+    /// }
+    ///
+    /// // offset non zero & no more fragments will come -> fragmented
+    /// {
+    ///     let header = Ipv6FragmentHeader::new(UDP, 1.try_into().unwrap(), false, 123);
+    ///     assert!(header.is_fragmenting_payload());
+    /// }
+    /// ```
+    #[inline]
+    pub fn is_fragmenting_payload(&self) -> bool {
+        self.more_fragments || (0 != self.fragment_offset.value())
+    }
+
+    /// Returns the serialized form of the header as a statically
+    /// sized byte array.
+    #[inline]
+    pub fn to_bytes(&self) -> [u8; 8] {
+        let fo_be: [u8; 2] = self.fragment_offset.value().to_be_bytes();
+        let id_be = self.identification.to_be_bytes();
+        [
+            self.next_header.0,
+            0,
+            (((fo_be[0] << 3) & 0b1111_1000u8) | ((fo_be[1] >> 5) & 0b0000_0111u8)),
+            ((fo_be[1] & 0b0001_1111u8)
+                | if self.more_fragments {
+                    0b1000_0000u8
+                } else {
+                    0
+                }),
+            id_be[0],
+            id_be[1],
+            id_be[2],
+            id_be[3],
+        ]
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{test_gens::*, *};
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+    use std::io::Cursor;
+
+    proptest! {
+        #[test]
+        fn debug(input in ipv6_fragment_any()) {
+            assert_eq!(
+                &format!(
+                    "Ipv6FragmentHeader {{ next_header: {:?}, fragment_offset: {:?}, more_fragments: {}, identification: {} }}",
+                    input.next_header,
+                    input.fragment_offset,
+                    input.more_fragments,
+                    input.identification
+                ),
+                &format!("{:?}", input)
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(input in ipv6_fragment_any()) {
+            assert_eq!(input, input.clone());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn new(
+            next_header in ip_number_any(),
+            fragment_offset in 0..IpFragOffset::MAX_U16,
+            more_fragments in any::<bool>(),
+            identification in any::<u32>(),
+        ) {
+            let a = Ipv6FragmentHeader::new(
+                next_header,
+                fragment_offset.try_into().unwrap(),
+                more_fragments,
+                identification
+            );
+            assert_eq!(next_header, a.next_header);
+            assert_eq!(fragment_offset, a.fragment_offset.value());
+            assert_eq!(more_fragments, a.more_fragments);
+            assert_eq!(identification, a.identification);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            input in ipv6_fragment_any(),
+            dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
+        ) {
+            // serialize
+            let mut buffer: Vec<u8> = Vec::with_capacity(8 + dummy_data.len());
+            input.write(&mut buffer).unwrap();
+            buffer.extend(&dummy_data[..]);
+
+            // calls with a valid result
+            {
+                let (result, rest) = Ipv6FragmentHeader::from_slice(&buffer[..]).unwrap();
+                assert_eq!(input, result);
+                assert_eq!(&buffer[8..], rest);
+            }
+            // call with not enough data in the slice
+            for len in 0..Ipv6FragmentHeader::LEN {
+                assert_eq!(
+                    Ipv6FragmentHeader::from_slice(&buffer[0..len]).unwrap_err(),
+                    err::LenError{
+                        required_len: Ipv6FragmentHeader::LEN,
+                        len: len,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::Ipv6FragHeader,
+                        layer_start_offset: 0,
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read(
+            input in ipv6_fragment_any(),
+            dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
+        ) {
+            use std::io::ErrorKind;
+
+            // serialize
+            let mut buffer: Vec<u8> = Vec::with_capacity(8 + dummy_data.len());
+            input.write(&mut buffer).unwrap();
+            buffer.extend(&dummy_data[..]);
+
+            // calls with a valid result
+            {
+                let mut cursor = Cursor::new(&buffer);
+                let result = Ipv6FragmentHeader::read(&mut cursor).unwrap();
+                assert_eq!(input, result);
+                assert_eq!(cursor.position(), 8);
+            }
+
+            // call with not enough data in the slice
+            for len in 0..Ipv6FragmentHeader::LEN {
+                let mut cursor = Cursor::new(&buffer[0..len]);
+                assert_eq!(
+                    Ipv6FragmentHeader::read(&mut cursor)
+                    .unwrap_err()
+                    .kind(),
+                    ErrorKind::UnexpectedEof
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write(input in ipv6_fragment_any()) {
+
+            // normal write
+            {
+                let mut buffer = Vec::with_capacity(8);
+                input.write(&mut buffer).unwrap();
+                assert_eq!(
+                    &buffer,
+                    &input.to_bytes()
+                );
+            }
+
+            // not enough memory for write
+            for len in 0..Ipv6FragmentHeader::LEN {
+                let mut buffer = [0u8;Ipv6FragmentHeader::LEN];
+                let mut cursor = Cursor::new(&mut buffer[..len]);
+                assert!(
+                    input.write(&mut cursor).is_err()
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header_len(input in ipv6_fragment_any()) {
+            assert_eq!(8, input.header_len());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn is_fragmenting_payload(
+            non_zero_offset in 1u16..0b0001_1111_1111_1111u16,
+            identification in any::<u32>(),
+            next_header in ip_number_any(),
+
+        ) {
+            // negative case
+            {
+                let header = Ipv6FragmentHeader {
+                    next_header,
+                    fragment_offset: 0.try_into().unwrap(),
+                    more_fragments: false,
+                    identification
+                };
+                assert!(false == header.is_fragmenting_payload());
+            }
+            // positive case (non zero offset)
+            {
+                let header = Ipv6FragmentHeader {
+                    next_header,
+                    fragment_offset: non_zero_offset.try_into().unwrap(),
+                    more_fragments: false,
+                    identification
+                };
+                assert!(header.is_fragmenting_payload());
+            }
+
+            // positive case (more fragments)
+            {
+                let header = Ipv6FragmentHeader {
+                    next_header,
+                    fragment_offset: 0.try_into().unwrap(),
+                    more_fragments: true,
+                    identification
+                };
+                assert!(header.is_fragmenting_payload());
+            }
+
+            // positive case (non zero offset & more fragments)
+            {
+                let header = Ipv6FragmentHeader {
+                    next_header,
+                    fragment_offset: non_zero_offset.try_into().unwrap(),
+                    more_fragments: true,
+                    identification
+                };
+                assert!(header.is_fragmenting_payload());
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_bytes(input in ipv6_fragment_any()) {
+
+            // normal write
+            {
+                let fragment_offset_be = input.fragment_offset.value().to_be_bytes();
+                let id_be = input.identification.to_be_bytes();
+                assert_eq!(
+                    &input.to_bytes(),
+                    &[
+                        input.next_header.0,
+                        0,
+                        (
+                            (fragment_offset_be[0] << 3 & 0b1111_1000u8) |
+                            (fragment_offset_be[1] >> 5 & 0b0000_0111u8)
+                        ),
+                        (
+                            (fragment_offset_be[1] & 0b0001_1111u8) |
+                            if input.more_fragments {
+                                0b1000_0000u8
+                            } else {
+                                0u8
+                            }
+                        ),
+                        id_be[0],
+                        id_be[1],
+                        id_be[2],
+                        id_be[3],
+                    ]
+                );
+            }
+        }
+    }
+}
diff --git a/src/net/ipv6_fragment_header_slice.rs b/src/net/ipv6_fragment_header_slice.rs
new file mode 100644
index 0000000..00d85b3
--- /dev/null
+++ b/src/net/ipv6_fragment_header_slice.rs
@@ -0,0 +1,336 @@
+use crate::*;
+use core::slice::from_raw_parts;
+
+/// Slice containing an IPv6 fragment header.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Ipv6FragmentHeaderSlice<'a> {
+    /// Slice containing the packet data.
+    slice: &'a [u8],
+}
+
+impl<'a> Ipv6FragmentHeaderSlice<'a> {
+    /// Creates a hop by hop header slice from a slice.
+    pub fn from_slice(slice: &'a [u8]) -> Result<Ipv6FragmentHeaderSlice<'a>, err::LenError> {
+        // the fragmentation header has the exact size of 8 bytes
+        if slice.len() < 8 {
+            Err(err::LenError {
+                required_len: 8,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::Ipv6FragHeader,
+                layer_start_offset: 0,
+            })
+        } else {
+            Ok(Ipv6FragmentHeaderSlice {
+                // SAFETY:
+                // Safe as slice length is checked to be at least 8 before this
+                // code can be reached.
+                slice: unsafe { from_raw_parts(slice.as_ptr(), 8) },
+            })
+        }
+    }
+
+    /// Creates a hop by hop header slice from a slice (assumes slice size & content was validated before).
+    ///
+    /// # Safety
+    ///
+    /// This function assumes that the passed slice has at least the length
+    /// of 8. If a slice with length less then 8 is passed to this function
+    /// the behavior will be undefined.
+    pub unsafe fn from_slice_unchecked(slice: &'a [u8]) -> Ipv6FragmentHeaderSlice<'a> {
+        debug_assert!(slice.len() >= Ipv6FragmentHeader::LEN);
+        // the fragmentation header has the exact size of 8 bytes
+        Ipv6FragmentHeaderSlice {
+            slice: from_raw_parts(slice.as_ptr(), Ipv6FragmentHeader::LEN),
+        }
+    }
+
+    /// Returns the slice containing the ipv6 fragment header.
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+
+    /// Returns the IP protocol number of the next header.
+    ///
+    /// See [IpNumber] or [ip_number] for a definition of the known values.
+    #[inline]
+    pub fn next_header(&self) -> IpNumber {
+        // SAFETY:
+        // Slice size checked to be at least 8 bytes in constructor.
+        IpNumber(unsafe { *self.slice.get_unchecked(0) })
+    }
+
+    /// Fragment offset
+    #[inline]
+    pub fn fragment_offset(&self) -> IpFragOffset {
+        unsafe {
+            // SAFETY: Safe as the resulting number is guaranteed to be only
+            // 13 bit long.
+            IpFragOffset::new_unchecked(u16::from_be_bytes([
+                // SAFETY:
+                // Slice size checked to be at least 8 bytes in constructor.
+                (*self.slice.get_unchecked(2) >> 3) & 0b0001_1111u8,
+                ((*self.slice.get_unchecked(2) << 5) & 0b1110_0000u8)
+                    | (*self.slice.get_unchecked(3) & 0b0001_1111u8),
+            ]))
+        }
+    }
+
+    /// True if more fragment packets will follow. False if this is the last packet.
+    #[inline]
+    pub fn more_fragments(&self) -> bool {
+        // SAFETY:
+        // Slice size checked to be at least 8 bytes in constructor.
+        unsafe { 0 != *self.slice.get_unchecked(3) & 0b1000_0000u8 }
+    }
+
+    /// Identifcation value generated by the source
+    pub fn identification(&self) -> u32 {
+        // SAFETY:
+        // Slice size checked to be at least 8 bytes in constructor.
+        unsafe { get_unchecked_be_u32(self.slice.as_ptr().add(4)) }
+    }
+
+    /// Checks if the fragment header actually fragments the packet.
+    ///
+    /// Returns false if the fragment offset is 0 and the more flag
+    /// is not set. Otherwise returns true.
+    ///
+    /// [RFC8200](https://datatracker.ietf.org/doc/html/rfc8200) explicitly
+    /// states that fragment headers that don't fragment the packet payload are
+    /// allowed. See the following quote from
+    /// RFC8200 page 32:
+    ///
+    /// > Revised the text to handle the case of fragments that are whole
+    /// > datagrams (i.e., both the Fragment Offset field and the M flag
+    /// > are zero).  If received, they should be processed as a
+    /// > reassembled packet.  Any other fragments that match should be
+    /// > processed independently.  The Fragment creation process was
+    /// > modified to not create whole datagram fragments (Fragment
+    /// > Offset field and the M flag are zero).  See
+    /// > [RFC6946](https://datatracker.ietf.org/doc/html/6946) and
+    /// > [RFC8021](https://datatracker.ietf.org/doc/html/rfc8021) for more
+    /// > information."
+    ///
+    /// ```
+    /// use etherparse::Ipv6FragmentHeaderSlice;
+    ///
+    /// {
+    ///     let slice = Ipv6FragmentHeaderSlice::from_slice(&[
+    ///         0, 0, 0, 0, // offset 0 & more_fragments not set
+    ///         1, 2, 3, 4,
+    ///     ]).unwrap();
+    ///     assert!(false == slice.is_fragmenting_payload());
+    /// }
+    ///
+    /// {
+    ///     let slice = Ipv6FragmentHeaderSlice::from_slice(&[
+    ///         0, 0, 0, 0b1000_0000u8, // more_fragments set
+    ///         1, 2, 3, 4,
+    ///     ]).unwrap();
+    ///     assert!(slice.is_fragmenting_payload());
+    /// }
+    ///
+    /// {
+    ///     let slice = Ipv6FragmentHeaderSlice::from_slice(&[
+    ///         0, 0, 1, 0, // non zero offset
+    ///         1, 2, 3, 4,
+    ///     ]).unwrap();
+    ///     assert!(slice.is_fragmenting_payload());
+    /// }
+    /// ```
+    #[inline]
+    pub fn is_fragmenting_payload(&self) -> bool {
+        // SAFETY:
+        // Slice size checked to be at least 8 bytes in constructor.
+        unsafe {
+            0 != *self.slice.get_unchecked(2) || 0 != (*self.slice.get_unchecked(3) & 0b1001_1111u8)
+            // exclude the reserved bytes
+        }
+    }
+
+    /// Decode some of the fields and copy the results to a
+    /// Ipv6FragmentHeader struct.
+    pub fn to_header(&self) -> Ipv6FragmentHeader {
+        Ipv6FragmentHeader {
+            next_header: self.next_header(),
+            fragment_offset: self.fragment_offset(),
+            more_fragments: self.more_fragments(),
+            identification: self.identification(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{test_gens::*, *};
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn debug(input in ipv6_fragment_any()) {
+            let bytes = input.to_bytes();
+            let slice = Ipv6FragmentHeaderSlice::from_slice(
+                &bytes
+            ).unwrap();
+            assert_eq!(
+                &format!(
+                    "Ipv6FragmentHeaderSlice {{ slice: {:?} }}",
+                    slice.slice()
+                ),
+                &format!("{:?}", slice)
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(input in ipv6_fragment_any()) {
+            let bytes = input.to_bytes();
+            let slice = Ipv6FragmentHeaderSlice::from_slice(
+                &bytes
+            ).unwrap();
+            assert_eq!(slice, slice.clone());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            input in ipv6_fragment_any(),
+            dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
+        ) {
+            // serialize
+            let mut buffer: Vec<u8> = Vec::with_capacity(8 + dummy_data.len());
+            input.write(&mut buffer).unwrap();
+            buffer.extend(&dummy_data[..]);
+
+            // calls with a valid result
+            {
+                let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer[..]).unwrap();
+                assert_eq!(slice.slice(), &buffer[..8]);
+            }
+
+            // call with not enough data in the slice
+            for len in 0..Ipv6FragmentHeader::LEN {
+                assert_eq!(
+                    Ipv6FragmentHeaderSlice::from_slice(&buffer[0..len]).unwrap_err(),
+                    err::LenError{
+                        required_len: 8,
+                        len: len,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::Ipv6FragHeader,
+                        layer_start_offset: 0,
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice_unchecked(
+            input in ipv6_fragment_any(),
+            dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
+        ) {
+                        // serialize
+            let mut buffer: Vec<u8> = Vec::with_capacity(8 + dummy_data.len());
+            input.write(&mut buffer).unwrap();
+            buffer.extend(&dummy_data[..]);
+
+            // calls with a valid result
+            unsafe {
+                let slice = Ipv6FragmentHeaderSlice::from_slice_unchecked(&buffer[..]);
+                assert_eq!(slice.slice(), &buffer[..8]);
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn getters(input in ipv6_fragment_any()) {
+            let buffer = input.to_bytes();
+            let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer[..]).unwrap();
+
+            assert_eq!(input.next_header, slice.next_header());
+            assert_eq!(input.fragment_offset, slice.fragment_offset());
+            assert_eq!(input.more_fragments, slice.more_fragments());
+            assert_eq!(input.identification, slice.identification());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn is_fragmenting_payload(
+            non_zero_offset in 1u16..0b0001_1111_1111_1111u16,
+            identification in any::<u32>(),
+            next_header in ip_number_any(),
+        ) {
+            // negative case
+            {
+                let header = Ipv6FragmentHeader {
+                    next_header,
+                    fragment_offset: 0.try_into().unwrap(),
+                    more_fragments: false,
+                    identification
+                };
+                // slice
+                let buffer = header.to_bytes();
+                let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap();
+                assert!(false == slice.is_fragmenting_payload());
+            }
+            // positive case (non zero offset)
+            {
+                let header = Ipv6FragmentHeader {
+                    next_header,
+                    fragment_offset: non_zero_offset.try_into().unwrap(),
+                    more_fragments: false,
+                    identification
+                };
+                // slice
+                let buffer = header.to_bytes();
+                let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap();
+                assert!(slice.is_fragmenting_payload());
+            }
+
+            // positive case (more fragments)
+            {
+                let header = Ipv6FragmentHeader {
+                    next_header,
+                    fragment_offset: 0.try_into().unwrap(),
+                    more_fragments: true,
+                    identification
+                };
+                // slice
+                let buffer = header.to_bytes();
+                let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap();
+                assert!(slice.is_fragmenting_payload());
+            }
+
+            // positive case (non zero offset & more fragments)
+            {
+                let header = Ipv6FragmentHeader {
+                    next_header,
+                    fragment_offset: non_zero_offset.try_into().unwrap(),
+                    more_fragments: true,
+                    identification
+                };
+                // slice
+                let buffer = header.to_bytes();
+                let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap();
+                assert!(slice.is_fragmenting_payload());
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_header(input in ipv6_fragment_any()) {
+            let buffer = input.to_bytes();
+            let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(input, slice.to_header());
+        }
+    }
+}
diff --git a/src/net/ipv6_header.rs b/src/net/ipv6_header.rs
new file mode 100644
index 0000000..7676466
--- /dev/null
+++ b/src/net/ipv6_header.rs
@@ -0,0 +1,1026 @@
+use crate::{err::ValueTooBigError, *};
+
+/// IPv6 header according to rfc8200.
+#[derive(Clone, Debug, Eq, PartialEq, Default)]
+pub struct Ipv6Header {
+    pub traffic_class: u8,
+    /// If non 0 serves as a hint to router and switches with multiple outbound paths that these packets should stay on the same path, so that they will not be reordered.
+    pub flow_label: Ipv6FlowLabel,
+    ///The length of the payload and extension headers in bytes (0 in case of jumbo payloads).
+    pub payload_length: u16,
+    /// IP protocol number specifying the next header or transport layer protocol.
+    ///
+    /// See [IpNumber] or [ip_number] for a definitions of ids.
+    pub next_header: IpNumber,
+    /// The number of hops the packet can take before it is discarded.
+    pub hop_limit: u8,
+    /// IPv6 source address
+    pub source: [u8; 16],
+    /// IPv6 destination address
+    pub destination: [u8; 16],
+}
+
+impl Ipv6Header {
+    /// Serialized size of an IPv6 header in bytes/octets.
+    pub const LEN: usize = 40;
+
+    #[deprecated(since = "0.14.0", note = "Use `Ipv6Header::LEN` instead")]
+    pub const SERIALIZED_SIZE: usize = Ipv6Header::LEN;
+
+    /// Renamed to `Ipv6Header::from_slice`
+    #[deprecated(since = "0.10.1", note = "Renamed to `Ipv6Header::from_slice`")]
+    #[inline]
+    pub fn read_from_slice(
+        slice: &[u8],
+    ) -> Result<(Ipv6Header, &[u8]), err::ipv6::HeaderSliceError> {
+        Ipv6Header::from_slice(slice)
+    }
+
+    /// Read an Ipv6Header from a slice and return the header & unused parts of the slice.
+    ///
+    /// Note that this function DOES NOT seperate the payload based on the length
+    /// payload_length present in the IPv6 header. It just returns the left over slice
+    /// after the header.
+    ///
+    /// If you want to have correctly seperated payload including the IP extension
+    /// headers use
+    ///
+    /// * [`crate::IpHeaders::from_ipv6_slice`] (decodes all the fields of the IP headers)
+    /// * [`crate::Ipv6Slice::from_slice`] (just identifies the ranges in the slice where
+    ///   the headers and payload are present)
+    ///
+    /// or
+    ///
+    /// * [`crate::IpHeaders::from_ipv6_slice_lax`]
+    /// * [`crate::Ipv6Slice::from_slice_lax`]
+    ///
+    /// for a laxer version which falls back to slice length when the `payload_length`
+    /// contains an inconsistent value.
+    #[inline]
+    pub fn from_slice(slice: &[u8]) -> Result<(Ipv6Header, &[u8]), err::ipv6::HeaderSliceError> {
+        Ok((
+            Ipv6HeaderSlice::from_slice(slice)?.to_header(),
+            &slice[Ipv6Header::LEN..],
+        ))
+    }
+
+    ///Reads an IPv6 header from the current position.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut T,
+    ) -> Result<Ipv6Header, err::ipv6::HeaderReadError> {
+        use err::ipv6::{HeaderError::*, HeaderReadError::*};
+
+        let mut value: [u8; 1] = [0; 1];
+        reader.read_exact(&mut value).map_err(Io)?;
+        let version_number = value[0] >> 4;
+        if 6 != version_number {
+            return Err(Content(UnexpectedVersion { version_number }));
+        }
+        Ipv6Header::read_without_version(reader, value[0] & 0xf).map_err(Io)
+    }
+
+    ///Reads an IPv6 header assuming the version & flow_label field have already been read.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read_without_version<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut T,
+        version_rest: u8,
+    ) -> Result<Ipv6Header, std::io::Error> {
+        let mut buffer: [u8; 8 + 32 - 1] = [0; 8 + 32 - 1];
+        reader.read_exact(&mut buffer[..])?;
+
+        Ok(Ipv6Header {
+            traffic_class: (version_rest << 4) | (buffer[0] >> 4),
+            flow_label: unsafe {
+                // SAFETY: Safe as the bitmask & 0 contant guarantee that the value
+                // does not exceed 20 bytes.
+                Ipv6FlowLabel::new_unchecked(u32::from_be_bytes([
+                    0,
+                    buffer[0] & 0b0000_1111,
+                    buffer[1],
+                    buffer[2],
+                ]))
+            },
+            payload_length: u16::from_be_bytes([buffer[3], buffer[4]]),
+            next_header: IpNumber(buffer[5]),
+            hop_limit: buffer[6],
+            #[rustfmt::skip]
+            source: [
+                buffer[7],   buffer[8],  buffer[9], buffer[10],
+                buffer[11], buffer[12], buffer[13], buffer[14],
+                buffer[15], buffer[16], buffer[17], buffer[18],
+                buffer[19], buffer[20], buffer[21], buffer[22],
+            ],
+            #[rustfmt::skip]
+            destination: [
+                buffer[23], buffer[24], buffer[25], buffer[26],
+                buffer[27], buffer[28], buffer[29], buffer[30],
+                buffer[31], buffer[32], buffer[33], buffer[34],
+                buffer[35], buffer[36], buffer[37], buffer[38],
+            ],
+        })
+    }
+
+    ///Takes a slice and skips an ipv6 header extensions and returns the next_header ip number & the slice past the header.
+    pub fn skip_header_extension_in_slice(
+        slice: &[u8],
+        next_header: IpNumber,
+    ) -> Result<(IpNumber, &[u8]), err::LenError> {
+        use crate::ip_number::*;
+
+        // verify that a ipv6 extension is present (before
+        // validating the slice length)
+        match next_header {
+            IPV6_FRAG | AUTH | IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_DEST_OPTIONS | MOBILITY
+            | HIP | SHIM6 => {}
+            _ => {
+                return Ok((next_header, slice));
+            }
+        }
+
+        if slice.len() >= 2 {
+            //determine the length
+            let len = match next_header {
+                IPV6_FRAG => 8,
+                AUTH => (usize::from(slice[1]) + 2) * 4,
+                IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_DEST_OPTIONS | MOBILITY | HIP | SHIM6 => {
+                    (usize::from(slice[1]) + 1) * 8
+                }
+                // not a ipv6 header extension that can be skipped
+                _ => unreachable!(),
+            };
+
+            if slice.len() < len {
+                Err(err::LenError {
+                    required_len: len,
+                    len: slice.len(),
+                    len_source: LenSource::Slice,
+                    layer: err::Layer::Ipv6ExtHeader,
+                    layer_start_offset: 0,
+                })
+            } else {
+                Ok((IpNumber(slice[0]), &slice[len..]))
+            }
+        } else {
+            Err(err::LenError {
+                required_len: 2,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::Ipv6ExtHeader,
+                layer_start_offset: 0,
+            })
+        }
+    }
+
+    /// Returns true if the given ip protocol number is a skippable header extension.
+    ///
+    /// A skippable header extension is an extension header for which it is known how
+    /// to determine the protocol number of the following header as well as how many
+    /// octets have to be skipped to reach the start of the following header.
+    pub fn is_skippable_header_extension(ip_protocol_number: IpNumber) -> bool {
+        use crate::ip_number::*;
+        //Note: EncapsulatingSecurityPayload & ExperimentalAndTesting0 can not be skipped
+        matches!(
+            ip_protocol_number,
+            IPV6_HOP_BY_HOP
+                | IPV6_ROUTE
+                | IPV6_FRAG
+                | AUTH
+                | IPV6_DEST_OPTIONS
+                | MOBILITY
+                | HIP
+                | SHIM6
+        )
+    }
+
+    ///Takes a slice & ip protocol number (identifying the first header type) and returns next_header id & the slice past after all ipv6 header extensions.
+    pub fn skip_all_header_extensions_in_slice(
+        slice: &[u8],
+        next_header: IpNumber,
+    ) -> Result<(IpNumber, &[u8]), err::LenError> {
+        let mut next_header = next_header;
+        let mut rest = slice;
+        let mut offset = 0;
+
+        loop {
+            let (n_id, n_rest) = Ipv6Header::skip_header_extension_in_slice(rest, next_header)
+                .map_err(|err| err.add_offset(offset))?;
+            offset = slice.len() - n_rest.len();
+
+            if n_rest.len() == rest.len() {
+                return Ok((next_header, rest));
+            } else {
+                next_header = n_id;
+                rest = n_rest;
+            }
+        }
+    }
+
+    ///Skips the ipv6 header extension and returns the next ip protocol number
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn skip_header_extension<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut T,
+        next_header: IpNumber,
+    ) -> Result<IpNumber, std::io::Error> {
+        use crate::ip_number::*;
+
+        let (next_header, rest_length) = match next_header {
+            IPV6_FRAG => {
+                let mut buf = [0; 1];
+                reader.read_exact(&mut buf)?;
+                (IpNumber(buf[0]), 7)
+            }
+            AUTH => {
+                let mut buf = [0; 2];
+                reader.read_exact(&mut buf)?;
+                (IpNumber(buf[0]), i64::from(buf[1]) * 4 + 6)
+            }
+            IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_DEST_OPTIONS | MOBILITY | HIP | SHIM6 => {
+                let mut buf = [0; 2];
+                reader.read_exact(&mut buf)?;
+                (IpNumber(buf[0]), i64::from(buf[1]) * 8 + 6)
+            }
+            // not a ipv6 header extension that can be skipped
+            _ => return Ok(next_header),
+        };
+
+        //Sadly seek does not return an error if the seek could not be fulfilled.
+        //Some implementations do not even truncate the returned position to the
+        //last valid one. std::io::Cursor for example just moves the position
+        //over the border of the given slice (e.g. returns position 15 even when
+        //the given slice contains only 1 element).
+        //The only option, to detect that we are in an invalid state, is to move the
+        //seek offset to one byte before the end and then execute a normal read to
+        //trigger an error.
+        reader.seek(std::io::SeekFrom::Current(rest_length - 1))?;
+        {
+            let mut buf = [0; 1];
+            reader.read_exact(&mut buf)?;
+        }
+        Ok(next_header)
+    }
+
+    ///Skips all ipv6 header extensions and returns the next ip protocol number
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn skip_all_header_extensions<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut T,
+        next_header: IpNumber,
+    ) -> Result<IpNumber, std::io::Error> {
+        let mut next_header = next_header;
+
+        loop {
+            if Ipv6Header::is_skippable_header_extension(next_header) {
+                next_header = Ipv6Header::skip_header_extension(reader, next_header)?;
+            } else {
+                return Ok(next_header);
+            }
+        }
+    }
+
+    ///Writes a given IPv6 header to the current position.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
+        writer.write_all(&self.to_bytes())
+    }
+
+    /// Return the ipv6 source address as an std::net::Ipv6Addr
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    #[inline]
+    pub fn source_addr(&self) -> std::net::Ipv6Addr {
+        std::net::Ipv6Addr::from(self.source)
+    }
+
+    /// Return the ipv6 destination address as an std::net::Ipv6Addr
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    #[inline]
+    pub fn destination_addr(&self) -> std::net::Ipv6Addr {
+        std::net::Ipv6Addr::from(self.destination)
+    }
+
+    /// Length of the serialized header in bytes.
+    ///
+    /// The function always returns the constant Ipv6Header::LEN
+    /// and exists to keep the methods consistent with other headers.
+    #[inline]
+    pub fn header_len(&self) -> usize {
+        Ipv6Header::LEN
+    }
+
+    /// Sets the field total_length based on the size of the payload and the options. Returns an error if the payload is too big to fit.
+    pub fn set_payload_length(&mut self, size: usize) -> Result<(), ValueTooBigError<usize>> {
+        use crate::err::ValueType;
+        // check that the total length fits into the field
+        const MAX_PAYLOAD_LENGTH: usize = u16::MAX as usize;
+        if MAX_PAYLOAD_LENGTH < size {
+            return Err(ValueTooBigError {
+                actual: size,
+                max_allowed: MAX_PAYLOAD_LENGTH,
+                value_type: ValueType::Ipv6PayloadLength,
+            });
+        }
+
+        self.payload_length = size as u16;
+        Ok(())
+    }
+
+    /// Returns the serialized form of the header as a statically
+    /// sized byte array.
+    #[rustfmt::skip]
+    pub fn to_bytes(&self) -> [u8;Ipv6Header::LEN] {
+        // serialize header
+        let flow_label_be = self.flow_label.value().to_be_bytes();
+        let payload_len_be = self.payload_length.to_be_bytes();
+
+        [
+            (6 << 4) | (self.traffic_class >> 4),
+            (self.traffic_class << 4) | flow_label_be[1],
+            flow_label_be[2],
+            flow_label_be[3],
+            payload_len_be[0],
+            payload_len_be[1],
+            self.next_header.0,
+            self.hop_limit,
+            self.source[0], self.source[1], self.source[2], self.source[3],
+            self.source[4], self.source[5], self.source[6], self.source[7],
+            self.source[8], self.source[9], self.source[10], self.source[11],
+            self.source[12], self.source[13], self.source[14], self.source[15],
+            self.destination[0], self.destination[1], self.destination[2], self.destination[3],
+            self.destination[4], self.destination[5], self.destination[6], self.destination[7],
+            self.destination[8], self.destination[9], self.destination[10], self.destination[11],
+            self.destination[12], self.destination[13], self.destination[14], self.destination[15],
+        ]
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{
+        err::ipv6::HeaderError::*, err::ipv6::HeaderSliceError::*, ip_number::*, test_gens::*, *,
+    };
+    use alloc::format;
+    use arrayvec::ArrayVec;
+    use proptest::*;
+    use std::io::Cursor;
+
+    #[test]
+    fn default() {
+        let header: Ipv6Header = Default::default();
+        assert_eq!(0, header.traffic_class);
+        assert_eq!(0, header.flow_label.value());
+        assert_eq!(0, header.payload_length);
+        assert_eq!(255, header.next_header.0);
+        assert_eq!(0, header.hop_limit);
+        assert_eq!([0u8; 16], header.source);
+        assert_eq!([0u8; 16], header.destination);
+    }
+
+    #[test]
+    fn debug() {
+        let header: Ipv6Header = Default::default();
+        assert_eq!(
+            format!("{:?}", header),
+            format!(
+                "Ipv6Header {{ traffic_class: {}, flow_label: {:?}, payload_length: {}, next_header: {:?}, hop_limit: {}, source: {:?}, destination: {:?} }}",
+                header.traffic_class,
+                header.flow_label,
+                header.payload_length,
+                header.next_header,
+                header.hop_limit,
+                header.source,
+                header.destination
+            )
+        );
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(header in ipv6_any()) {
+            assert_eq!(header.clone(), header);
+        }
+    }
+
+    proptest! {
+        #[test]
+        #[allow(deprecated)]
+        fn read_from_slice(
+            header in ipv6_any(),
+            bad_version in 0..=0b1111u8
+        ) {
+            // ok read
+            {
+                let bytes = header.to_bytes();
+                let (actual, rest) = Ipv6Header::read_from_slice(&bytes).unwrap();
+                assert_eq!(header, actual);
+                assert_eq!(rest, &[]);
+            }
+
+            // version error
+            if bad_version != 6 {
+                let mut bytes = header.to_bytes();
+                // inject a bad version number
+                bytes[0] = (0b1111 & bytes[0]) | (bad_version << 4);
+
+                assert_eq!(
+                    Ipv6Header::read_from_slice(&bytes).unwrap_err(),
+                    Content(UnexpectedVersion{ version_number: bad_version })
+                );
+            }
+
+            // length error
+            {
+                let bytes = header.to_bytes();
+                for len in 0..bytes.len() {
+                    assert_eq!(
+                        Ipv6Header::read_from_slice(&bytes[..len])
+                            .unwrap_err(),
+                        Len(err::LenError{
+                            required_len: Ipv6Header::LEN,
+                            len: len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv6Header,
+                            layer_start_offset: 0,
+                        })
+                    );
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            header in ipv6_any(),
+            bad_version in 0..=0b1111u8
+        ) {
+            // ok read
+            {
+                let bytes = header.to_bytes();
+                let (actual, rest) = Ipv6Header::from_slice(&bytes).unwrap();
+                assert_eq!(header, actual);
+                assert_eq!(rest, &[]);
+            }
+
+            // version error
+            if bad_version != 6 {
+                let mut bytes = header.to_bytes();
+                // inject a bad version number
+                bytes[0] = (0b1111 & bytes[0]) | (bad_version << 4);
+
+                assert_eq!(
+                    Ipv6Header::from_slice(&bytes).unwrap_err(),
+                    Content(UnexpectedVersion{ version_number: bad_version })
+                );
+            }
+
+            // length error
+            {
+                let bytes = header.to_bytes();
+                for len in 0..bytes.len() {
+                    assert_eq!(
+                        Ipv6Header::from_slice(&bytes[..len])
+                            .unwrap_err(),
+                        Len(err::LenError{
+                            required_len: Ipv6Header::LEN,
+                            len: len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv6Header,
+                            layer_start_offset: 0,
+                        })
+                    );
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read(
+            header in ipv6_any(),
+            bad_version in 0..=0b1111u8
+        ) {
+            use err::ipv6::HeaderError::*;
+
+            // ok read
+            {
+                let bytes = header.to_bytes();
+                let mut cursor = Cursor::new(&bytes[..]);
+                let actual = Ipv6Header::read(&mut cursor).unwrap();
+                assert_eq!(header, actual);
+                assert_eq!(cursor.position(), bytes.len() as u64);
+            }
+
+            // version error
+            if bad_version != 6 {
+                let mut bytes = header.to_bytes();
+                // inject a bad version number
+                bytes[0] = (0b1111 & bytes[0]) | (bad_version << 4);
+
+                let mut cursor = Cursor::new(&bytes[..]);
+                assert_eq!(
+                    Ipv6Header::read(&mut cursor)
+                        .unwrap_err()
+                        .content_error()
+                        .unwrap(),
+                    UnexpectedVersion {
+                        version_number: bad_version,
+                    }
+                );
+            }
+
+            // io error
+            {
+                let bytes = header.to_bytes();
+                for len in 0..bytes.len() {
+                    let mut cursor = Cursor::new(&bytes[..len]);
+                    assert!(Ipv6Header::read(&mut cursor).is_err());
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read_without_version(header in ipv6_any()) {
+            // ok read
+            {
+                let bytes = header.to_bytes();
+                let mut cursor = Cursor::new(&bytes[1..]);
+                let actual = Ipv6Header::read_without_version(&mut cursor, bytes[0] & 0xf).unwrap();
+                assert_eq!(header, actual);
+                assert_eq!(cursor.position(), bytes.len() as u64 - 1);
+            }
+
+            // io error
+            {
+                let bytes = header.to_bytes();
+                for len in 1..bytes.len() {
+                    let mut cursor = Cursor::new(&bytes[1..len]);
+                    assert!(Ipv6Header::read_without_version(&mut cursor, bytes[0] & 0xf).is_err());
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn skip_header_extension_in_slice(
+            generic in ipv6_raw_ext_any(),
+            frag in ipv6_fragment_any(),
+            auth in ip_auth_any()
+        ) {
+            const GENERICS: [IpNumber;7] = [
+                IPV6_HOP_BY_HOP,
+                IPV6_DEST_OPTIONS,
+                IPV6_ROUTE,
+                IPV6_DEST_OPTIONS,
+                MOBILITY,
+                HIP,
+                SHIM6,
+            ];
+
+            // generic headers
+            for g in GENERICS {
+                let bytes = generic.to_bytes();
+                // ok case
+                {
+                    let (next, rest) = Ipv6Header::skip_header_extension_in_slice(&bytes, g).unwrap();
+                    assert_eq!(next, generic.next_header);
+                    assert_eq!(rest, &[]);
+                }
+                // length error
+                for len in 0..bytes.len() {
+                    assert_eq!(
+                        Ipv6Header::skip_header_extension_in_slice(&bytes[..len], g).unwrap_err(),
+                        err::LenError {
+                            required_len: if len < 2 {
+                                2
+                            } else {
+                                bytes.len()
+                            },
+                            len: len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv6ExtHeader,
+                            layer_start_offset: 0,
+                        }
+                    );
+                }
+            }
+            // frag header
+            {
+                let bytes = frag.to_bytes();
+                // ok case
+                {
+                    let (next, rest) = Ipv6Header::skip_header_extension_in_slice(&bytes, IPV6_FRAG).unwrap();
+                    assert_eq!(next, frag.next_header);
+                    assert_eq!(rest, &[]);
+                }
+                // length error
+                for len in 0..bytes.len() {
+                    assert_eq!(
+                        Ipv6Header::skip_header_extension_in_slice(&bytes[..len], IPV6_FRAG).unwrap_err(),
+                        err::LenError {
+                            required_len: if len < 2 {
+                                2
+                            } else {
+                                bytes.len()
+                            },
+                            len: len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv6ExtHeader,
+                            layer_start_offset: 0,
+                        }
+                    );
+                }
+            }
+
+            // auth header
+            {
+                let bytes = auth.to_bytes();
+                // ok case
+                {
+                    let (next, rest) = Ipv6Header::skip_header_extension_in_slice(&bytes, AUTH).unwrap();
+                    assert_eq!(next, auth.next_header);
+                    assert_eq!(rest, &[]);
+                }
+                // length error
+                for len in 0..bytes.len() {
+                    assert_eq!(
+                        Ipv6Header::skip_header_extension_in_slice(&bytes[..len], AUTH).unwrap_err(),
+                        err::LenError {
+                            required_len: if len < 2 {
+                                2
+                            } else {
+                                bytes.len()
+                            },
+                            len: len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv6ExtHeader,
+                            layer_start_offset: 0,
+                        }
+                    );
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn is_skippable_header_extension() {
+        for i in 0..0xffu8 {
+            let expected = match IpNumber(i) {
+                IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_FRAG | AUTH | IPV6_DEST_OPTIONS | MOBILITY
+                | HIP | SHIM6 => true,
+                _ => false,
+            };
+            assert_eq!(
+                expected,
+                Ipv6Header::is_skippable_header_extension(IpNumber(i))
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn skip_all_header_extensions_in_slice(
+            hop_by_hop in ipv6_raw_ext_any(),
+            dst_opt1 in ipv6_raw_ext_any(),
+            route in ipv6_raw_ext_any(),
+            frag in ipv6_fragment_any(),
+            auth in ip_auth_any(),
+            dst_opt2 in ipv6_raw_ext_any(),
+            mobility in ipv6_raw_ext_any(),
+            hip in ipv6_raw_ext_any(),
+            shim6 in ipv6_raw_ext_any()
+        ) {
+            // no extension header
+            {
+                let (next, rest) = Ipv6Header::skip_all_header_extensions_in_slice(&[], UDP).unwrap();
+                assert_eq!(UDP, next);
+                assert_eq!(rest, &[]);
+            }
+
+            // setup a buffer with all extension headers present
+            let buffer = {
+                let mut buffer = ArrayVec::<u8, {
+                    Ipv6RawExtHeader::MAX_LEN * 8 + IpAuthHeader::MAX_LEN
+                }>::new();
+
+                // based on RFC 8200 4.1. Extension Header Order
+                // & IANA https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml
+                //
+                // IPV6_HOP_BY_HOP,
+                // IPV6_DEST_OPTIONS,
+                // IPV6_ROUTE,
+                // IPV6_FRAG,
+                // AUTH,
+                // IPV6_DEST_OPTIONS,
+                // MOBILITY,
+                // HIP,
+                // SHIM6,
+
+                let mut hop_by_hop = hop_by_hop.clone();
+                hop_by_hop.next_header = IPV6_DEST_OPTIONS;
+                buffer.extend(hop_by_hop.to_bytes());
+
+                let mut dst_opt1 = dst_opt1.clone();
+                dst_opt1.next_header = IPV6_ROUTE;
+                buffer.extend(dst_opt1.to_bytes());
+
+                let mut route = route.clone();
+                route.next_header = IPV6_FRAG;
+                buffer.extend(route.to_bytes());
+
+                let mut frag = frag.clone();
+                frag.next_header = AUTH;
+                buffer.extend(frag.to_bytes());
+
+                let mut auth = auth.clone();
+                auth.next_header = IPV6_DEST_OPTIONS;
+                buffer.extend(auth.to_bytes());
+
+                let mut dst_opt2 = dst_opt2.clone();
+                dst_opt2.next_header = MOBILITY;
+                buffer.extend(dst_opt2.to_bytes());
+
+                let mut mobility = mobility.clone();
+                mobility.next_header = HIP;
+                buffer.extend(mobility.to_bytes());
+
+                let mut hip = hip.clone();
+                hip.next_header = SHIM6;
+                buffer.extend(hip.to_bytes());
+
+                let mut shim6 = shim6.clone();
+                shim6.next_header = TCP;
+                buffer.extend(shim6.to_bytes());
+
+                buffer
+            };
+
+            // ok skip case with all extension headers
+            {
+                let (next, rest) = Ipv6Header::skip_all_header_extensions_in_slice(&buffer, IPV6_HOP_BY_HOP).unwrap();
+                assert_eq!(next, TCP);
+                assert_eq!(rest, &[]);
+            }
+
+            // length error
+            {
+                let len_ranges: [usize;9] = [
+                    hop_by_hop.header_len(),
+                    dst_opt1.header_len(),
+                    route.header_len(),
+                    frag.header_len(),
+                    auth.header_len(),
+                    dst_opt2.header_len(),
+                    mobility.header_len(),
+                    hip.header_len(),
+                    shim6.header_len()
+                ];
+                let get_expected = |len: usize| -> usize{
+                    let mut curr = 0;
+                    for next in &len_ranges {
+                        if len < curr {
+                            break;
+                        }
+                        if len < curr + 2 {
+                            curr += 2;
+                            break;
+                        }
+                        curr += next;
+                    }
+                    curr
+                };
+
+                let get_offset = |len: usize| -> usize{
+                    let mut curr = 0;
+                    for next in &len_ranges {
+                        if len < curr + next {
+                            break;
+                        }
+                        curr += next;
+                    }
+                    curr
+                };
+
+                for len in 0..buffer.len() {
+                    assert_eq!(
+                        Ipv6Header::skip_all_header_extensions_in_slice(&buffer[..len], IPV6_HOP_BY_HOP)
+                            .unwrap_err(),
+                        err::LenError {
+                            required_len: get_expected(len) - get_offset(len),
+                            len: len - get_offset(len),
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv6ExtHeader,
+                            layer_start_offset: get_offset(len),
+                        }
+                    );
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn skip_header_extension() {
+        use crate::ip_number::*;
+        {
+            let buffer: [u8; 8] = [0; 8];
+            let mut cursor = Cursor::new(&buffer);
+            assert_eq!(
+                Ipv6Header::skip_header_extension(&mut cursor, ICMP).unwrap(),
+                ICMP
+            );
+            assert_eq!(0, cursor.position());
+        }
+        {
+            let buffer: [u8; 8] = [0; 8];
+            let mut cursor = Cursor::new(&buffer);
+            assert_eq!(
+                Ipv6Header::skip_header_extension(&mut cursor, IPV6_HOP_BY_HOP).unwrap(),
+                IpNumber(0)
+            );
+            assert_eq!(8, cursor.position());
+        }
+        {
+            #[rustfmt::skip]
+            let buffer: [u8; 8 * 3] = [
+                4, 2, 0, 0, 0, 0, 0, 0,
+                0, 0, 0, 0, 0, 0, 0, 0,
+                0, 0, 0, 0, 0, 0, 0, 0,
+            ];
+            let mut cursor = Cursor::new(&buffer);
+            assert_eq!(
+                Ipv6Header::skip_header_extension(&mut cursor, IPV6_ROUTE).unwrap(),
+                IpNumber(4)
+            );
+            assert_eq!(8 * 3, cursor.position());
+        }
+        {
+            //fragmentation header has a fixed size -> the 2 should be ignored
+            #[rustfmt::skip]
+            let buffer: [u8; 8 * 3] = [
+                4, 2, 0, 0, 0, 0, 0, 0,
+                0, 0, 0, 0, 0, 0, 0, 0,
+                0, 0, 0, 0, 0, 0, 0, 0,
+            ];
+            let mut cursor = Cursor::new(&buffer);
+            assert_eq!(
+                Ipv6Header::skip_header_extension(&mut cursor, IPV6_FRAG).unwrap(),
+                IpNumber(4)
+            );
+            assert_eq!(8, cursor.position());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn skip_all_header_extensions(
+            hop_by_hop in ipv6_raw_ext_any(),
+            dst_opt1 in ipv6_raw_ext_any(),
+            route in ipv6_raw_ext_any(),
+            frag in ipv6_fragment_any(),
+            auth in ip_auth_any(),
+            dst_opt2 in ipv6_raw_ext_any(),
+            mobility in ipv6_raw_ext_any(),
+            hip in ipv6_raw_ext_any(),
+            shim6 in ipv6_raw_ext_any()
+        ) {
+            // no extension header
+            {
+                let mut cursor = Cursor::new(&[]);
+                let next = Ipv6Header::skip_all_header_extensions(&mut cursor, UDP).unwrap();
+                assert_eq!(UDP, next);
+                assert_eq!(0, cursor.position());
+            }
+
+            // setup a buffer with all extension headers present
+            let buffer = {
+                let mut buffer = ArrayVec::<u8, {
+                    Ipv6RawExtHeader::MAX_LEN * 8 + IpAuthHeader::MAX_LEN
+                }>::new();
+
+                // based on RFC 8200 4.1. Extension Header Order
+                // & IANA https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml
+                //
+                // IPV6_HOP_BY_HOP,
+                // IPV6_DEST_OPTIONS,
+                // IPV6_ROUTE,
+                // IPV6_FRAG,
+                // AUTH,
+                // IPV6_DEST_OPTIONS,
+                // MOBILITY,
+                // HIP,
+                // SHIM6,
+
+                let mut hop_by_hop = hop_by_hop.clone();
+                hop_by_hop.next_header = IPV6_DEST_OPTIONS;
+                buffer.extend(hop_by_hop.to_bytes());
+
+                let mut dst_opt1 = dst_opt1.clone();
+                dst_opt1.next_header = IPV6_ROUTE;
+                buffer.extend(dst_opt1.to_bytes());
+
+                let mut route = route.clone();
+                route.next_header = IPV6_FRAG;
+                buffer.extend(route.to_bytes());
+
+                let mut frag = frag.clone();
+                frag.next_header = AUTH;
+                buffer.extend(frag.to_bytes());
+
+                let mut auth = auth.clone();
+                auth.next_header = IPV6_DEST_OPTIONS;
+                buffer.extend(auth.to_bytes());
+
+                let mut dst_opt2 = dst_opt2.clone();
+                dst_opt2.next_header = MOBILITY;
+                buffer.extend(dst_opt2.to_bytes());
+
+                let mut mobility = mobility.clone();
+                mobility.next_header = HIP;
+                buffer.extend(mobility.to_bytes());
+
+                let mut hip = hip.clone();
+                hip.next_header = SHIM6;
+                buffer.extend(hip.to_bytes());
+
+                let mut shim6 = shim6.clone();
+                shim6.next_header = TCP;
+                buffer.extend(shim6.to_bytes());
+
+                buffer
+            };
+
+            // ok skip case with all extension headers
+            {
+                let mut cursor = Cursor::new(&buffer);
+                let last = Ipv6Header::skip_all_header_extensions(&mut cursor, IPV6_HOP_BY_HOP).unwrap();
+                assert_eq!(last, TCP);
+                assert_eq!(cursor.position(), buffer.len() as u64);
+            }
+
+            // length error
+            for len in 0..buffer.len() {
+                let mut cursor = Cursor::new(&buffer[..len]);
+                assert!(
+                    Ipv6Header::skip_all_header_extensions(&mut cursor, IPV6_HOP_BY_HOP)
+                    .is_err()
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write(header in ipv6_any()) {
+            let mut buffer = [0u8;Ipv6Header::LEN];
+            let len = {
+                let mut cursor = Cursor::new(&mut buffer[..]);
+                header.write(&mut cursor).unwrap();
+                cursor.position() as usize
+            };
+            assert_eq!(len, header.header_len());
+            assert_eq!(
+                Ipv6Header::from_slice(&buffer[..len]).unwrap().0,
+                header
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn source_addr(header in ipv6_any()) {
+            assert_eq!(
+                header.source_addr().octets(),
+                header.source
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn destination_addr(header in ipv6_any()) {
+            assert_eq!(
+                header.destination_addr().octets(),
+                header.destination
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_bytes(header in ipv6_any()) {
+            let bytes = header.to_bytes();
+            assert_eq!(
+                Ipv6Header::from_slice(&bytes).unwrap().0,
+                header
+            );
+        }
+    }
+}
diff --git a/src/net/ipv6_header_slice.rs b/src/net/ipv6_header_slice.rs
new file mode 100644
index 0000000..eea301e
--- /dev/null
+++ b/src/net/ipv6_header_slice.rs
@@ -0,0 +1,313 @@
+use crate::*;
+use core::slice::from_raw_parts;
+
+/// A slice containing an ipv6 header of a network package.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct Ipv6HeaderSlice<'a> {
+    slice: &'a [u8],
+}
+
+impl<'a> Ipv6HeaderSlice<'a> {
+    /// Creates a slice containing an ipv6 header (without header extensions).
+    pub fn from_slice(slice: &'a [u8]) -> Result<Ipv6HeaderSlice<'a>, err::ipv6::HeaderSliceError> {
+        use err::ipv6::{HeaderError::*, HeaderSliceError::*};
+
+        // check length
+        if slice.len() < Ipv6Header::LEN {
+            return Err(Len(err::LenError {
+                required_len: Ipv6Header::LEN,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::Ipv6Header,
+                layer_start_offset: 0,
+            }));
+        }
+
+        // read version & ihl
+        //
+        // SAFETY:
+        // This is safe as the slice len is checked to be
+        // at least 40 bytes at the start of the function.
+        let version_number = unsafe { slice.get_unchecked(0) >> 4 };
+
+        // check version
+        if 6 != version_number {
+            return Err(Content(UnexpectedVersion { version_number }));
+        }
+
+        // all good
+        Ok(Ipv6HeaderSlice {
+            // SAFETY:
+            // This is safe as the slice length is checked to be
+            // at least Ipv6Header::LEN (40)
+            // at the start of the function.
+            slice: unsafe { from_raw_parts(slice.as_ptr(), Ipv6Header::LEN) },
+        })
+    }
+
+    /// Converts the given slice into a ipv6 header slice WITHOUT any
+    /// checks to ensure that the data present is an ipv4 header or that the
+    /// slice length is matching the header length.
+    ///
+    /// If you are not sure what this means, use [`Ipv6HeaderSlice::from_slice`]
+    /// instead.
+    ///
+    /// # Safety
+    ///
+    /// It must ensured that the slice length is at least [`Ipv6Header::LEN`].
+    #[inline]
+    pub(crate) unsafe fn from_slice_unchecked(slice: &[u8]) -> Ipv6HeaderSlice {
+        Ipv6HeaderSlice { slice }
+    }
+
+    /// Returns the slice containing the ipv6 header
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+
+    /// Read the "version" field from the slice (should be 6).
+    #[inline]
+    pub fn version(&self) -> u8 {
+        // SAFETY:
+        // Safe as the slice length is set to
+        // Ipv6Header::LEN (40) during construction
+        // of the struct.
+        unsafe { *self.slice.get_unchecked(0) >> 4 }
+    }
+
+    /// Read the "traffic class" field from the slice.
+    #[inline]
+    pub fn traffic_class(&self) -> u8 {
+        // SAFETY:
+        // Safe as the slice length is set to
+        // Ipv6Header::LEN (40) during construction
+        // of the struct.
+        unsafe { (self.slice.get_unchecked(0) << 4) | (self.slice.get_unchecked(1) >> 4) }
+    }
+
+    /// Read the "flow label" field from the slice.
+    #[inline]
+    pub fn flow_label(&self) -> Ipv6FlowLabel {
+        unsafe {
+            // SAFETY:
+            // Slice access safe as the slice length is set to Ipv6Header::LEN (40)
+            // during construction of the struct.
+            // Conversion to flow label safe as the bitmask & 0 constant guarantee
+            // that the value does not exceed 20 bits.
+            Ipv6FlowLabel::new_unchecked(u32::from_be_bytes([
+                0,
+                *self.slice.get_unchecked(1) & 0xf,
+                *self.slice.get_unchecked(2),
+                *self.slice.get_unchecked(3),
+            ]))
+        }
+    }
+
+    /// Read the "payload length" field from  the slice. The length should contain the length of all extension headers and payload.
+    #[inline]
+    pub fn payload_length(&self) -> u16 {
+        // SAFETY:
+        // Safe as the slice length is set to
+        // Ipv6Header::LEN (40) during construction
+        // of the struct.
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(4)) }
+    }
+
+    /// Read the "next header" field from the slice.
+    ///
+    /// The next header value specifies what the next header or transport
+    /// layer protocol is (see [IpNumber] or [ip_number] for a definitions of ids).
+    #[inline]
+    pub fn next_header(&self) -> IpNumber {
+        // SAFETY:
+        // Safe as the slice length is set to
+        // Ipv6Header::LEN (40) during construction
+        // of the struct.
+        IpNumber(unsafe { *self.slice.get_unchecked(6) })
+    }
+
+    /// Read the "hop limit" field from the slice. The hop limit specifies the number of hops the packet can take before it is discarded.
+    #[inline]
+    pub fn hop_limit(&self) -> u8 {
+        // SAFETY:
+        // Safe as the slice length is set to
+        // Ipv6Header::LEN (40) during construction
+        // of the struct.
+        unsafe { *self.slice.get_unchecked(7) }
+    }
+
+    /// Returns a slice containing the IPv6 source address.
+    #[inline]
+    pub fn source(&self) -> [u8; 16] {
+        // SAFETY:
+        // Safe as the slice length is set to
+        // Ipv6Header::LEN (40) during construction
+        // of the struct.
+        unsafe { get_unchecked_16_byte_array(self.slice.as_ptr().add(8)) }
+    }
+
+    /// Return the ipv6 source address as an std::net::Ipv6Addr
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    #[inline]
+    pub fn source_addr(&self) -> std::net::Ipv6Addr {
+        std::net::Ipv6Addr::from(self.source())
+    }
+
+    /// Returns a slice containing the IPv6 destination address.
+    #[inline]
+    pub fn destination(&self) -> [u8; 16] {
+        // SAFETY:
+        // Safe as the slice length is set to
+        // Ipv6Header::LEN (40) during construction
+        // of the struct.
+        unsafe { get_unchecked_16_byte_array(self.slice.as_ptr().add(24)) }
+    }
+
+    /// Return the ipv6 destination address as an std::net::Ipv6Addr
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    #[inline]
+    pub fn destination_addr(&self) -> std::net::Ipv6Addr {
+        std::net::Ipv6Addr::from(self.destination())
+    }
+
+    /// Decode all the fields and copy the results to a Ipv6Header struct
+    pub fn to_header(&self) -> Ipv6Header {
+        Ipv6Header {
+            traffic_class: self.traffic_class(),
+            flow_label: self.flow_label(),
+            payload_length: self.payload_length(),
+            next_header: self.next_header(),
+            hop_limit: self.hop_limit(),
+            source: self.source(),
+            destination: self.destination(),
+        }
+    }
+
+    /// Returns the length of the IPv6 header in bytes (same as [`crate::Ipv6Header::LEN`]).
+    pub const fn header_len(&self) -> usize {
+        Ipv6Header::LEN
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{err::ipv6::HeaderError::*, err::ipv6::HeaderSliceError::*, test_gens::*, *};
+    use alloc::format;
+    use proptest::*;
+
+    #[test]
+    fn debug() {
+        let header: Ipv6Header = Default::default();
+        let bytes = header.to_bytes();
+        let slice = Ipv6HeaderSlice::from_slice(&bytes).unwrap();
+        assert_eq!(
+            format!("{:?}", slice),
+            format!("Ipv6HeaderSlice {{ slice: {:?} }}", &bytes[..])
+        );
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(header in ipv6_any()) {
+            let bytes = header.to_bytes();
+            let slice = Ipv6HeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(slice.clone(), slice);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            header in ipv6_any(),
+            bad_version in 0..=0b1111u8)
+        {
+            // ok read
+            {
+                let bytes = header.to_bytes();
+                let actual = Ipv6HeaderSlice::from_slice(&bytes).unwrap();
+                assert_eq!(actual.slice(), &bytes[..]);
+            }
+
+            // version error
+            if bad_version != 6 {
+                let mut bytes = header.to_bytes();
+                // inject a bad version number
+                bytes[0] = (0b1111 & bytes[0]) | (bad_version << 4);
+
+                assert_eq!(
+                    Ipv6HeaderSlice::from_slice(&bytes).unwrap_err(),
+                    Content(UnexpectedVersion{ version_number: bad_version })
+                );
+            }
+
+            // length error
+            {
+                let bytes = header.to_bytes();
+                for len in 0..bytes.len() {
+                    assert_eq!(
+                        Ipv6HeaderSlice::from_slice(&bytes[..len])
+                            .unwrap_err(),
+                        Len(err::LenError{
+                            required_len: Ipv6Header::LEN,
+                            len: len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv6Header,
+                            layer_start_offset: 0,
+                        })
+                    );
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice_unchecked(header in ipv6_any()) {
+            let bytes = header.to_bytes();
+            let actual = unsafe {
+                Ipv6HeaderSlice::from_slice_unchecked(&bytes)
+            };
+            assert_eq!(actual.slice(), &bytes[..]);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn getters(header in ipv6_any()) {
+            let bytes = header.to_bytes();
+            let actual = Ipv6HeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(actual.slice(), &bytes[..]);
+            assert_eq!(actual.version(), 6);
+            assert_eq!(actual.traffic_class(), header.traffic_class);
+            assert_eq!(actual.flow_label(), header.flow_label);
+            assert_eq!(actual.payload_length(), header.payload_length);
+            assert_eq!(actual.next_header(), header.next_header);
+            assert_eq!(actual.hop_limit(), header.hop_limit);
+            assert_eq!(actual.source(), header.source);
+            assert_eq!(actual.destination(), header.destination);
+        }
+    }
+
+    #[cfg(feature = "std")]
+    proptest! {
+        #[test]
+        fn getters_std(header in ipv6_any()) {
+            let bytes = header.to_bytes();
+            let actual = Ipv6HeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(actual.source_addr(), std::net::Ipv6Addr::from(header.source));
+            assert_eq!(actual.destination_addr(), std::net::Ipv6Addr::from(header.destination));
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_header(header in ipv6_any()) {
+            let bytes = header.to_bytes();
+            let actual = Ipv6HeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(actual.to_header(), header);
+        }
+    }
+}
diff --git a/src/net/ipv6_raw_ext_header.rs b/src/net/ipv6_raw_ext_header.rs
new file mode 100644
index 0000000..2a664ac
--- /dev/null
+++ b/src/net/ipv6_raw_ext_header.rs
@@ -0,0 +1,500 @@
+use super::super::*;
+use crate::err::ipv6_exts::ExtPayloadLenError;
+use arrayvec::ArrayVec;
+use core::fmt::{Debug, Formatter};
+
+/// Deprecated. Use [Ipv6RawExtHeader] instead.
+#[deprecated(
+    since = "0.14.0",
+    note = "Please use the type Ipv6RawExtHeader instead"
+)]
+pub type Ipv6RawExtensionHeader = Ipv6RawExtHeader;
+
+/// Raw IPv6 extension header (undecoded payload).
+///
+/// IPv6 extension header with only minimal data interpretation. NOTE only ipv6 header
+/// extensions with the first two bytes representing the next header and the header length
+/// in 8-octets (- 8 octets) can be represented with this struct. This excludes the "Authentication
+/// Header" (AH) and "Encapsulating Security Payload" (ESP).
+///
+/// The following headers can be represented in a [`Ipv6RawExtHeader`]:
+/// * Hop by Hop
+/// * Destination Options
+/// * Routing
+/// * Mobility
+/// * Host Identity Protocol
+/// * Shim6 Protocol
+#[derive(Clone)]
+pub struct Ipv6RawExtHeader {
+    /// IP protocol number specifying the next header or transport layer protocol.
+    ///
+    /// See [IpNumber] or [ip_number] for a definition of the known values.
+    pub next_header: IpNumber,
+    /// Length of the extension header in 8 octets (minus the first 8 octets).
+    header_length: u8,
+    //// The data contained in the extension header (excluding next_header & hdr length).
+    payload_buffer: [u8; 0xff * 8 + 6],
+}
+
+impl Debug for Ipv6RawExtHeader {
+    fn fmt(&self, f: &mut Formatter) -> Result<(), core::fmt::Error> {
+        let mut s = f.debug_struct("Ipv6RawExtHeader");
+        s.field("next_header", &self.next_header);
+        s.field("payload", &self.payload());
+        s.finish()
+    }
+}
+
+impl PartialEq for Ipv6RawExtHeader {
+    fn eq(&self, other: &Self) -> bool {
+        self.next_header == other.next_header && self.payload() == other.payload()
+    }
+}
+
+impl Eq for Ipv6RawExtHeader {}
+
+impl Default for Ipv6RawExtHeader {
+    fn default() -> Self {
+        Ipv6RawExtHeader {
+            next_header: IpNumber(255),
+            header_length: 0,
+            payload_buffer: [0; 0xff * 8 + 6],
+        }
+    }
+}
+
+impl Ipv6RawExtHeader {
+    /// Minimum length of an raw IPv6 extension header in bytes/octets.
+    pub const MIN_LEN: usize = 8;
+
+    /// Maximum length of an raw IPv6 extension header in bytes/octets.
+    ///
+    /// This number is calculated by multiplying the maximum "hdr ext len"
+    /// (0xff) with 8 and adding 8. As RFC8200 states that "hdr ext len" is
+    /// defined as "8-bit unsigned integer. Length of the Hop-by-Hop Options
+    /// header in 8-octet units, not including the first 8 octets."
+    pub const MAX_LEN: usize = 8 + (8 * 0xff);
+
+    /// Minimum length of a [Ipv6RawExtHeader] payload
+    pub const MIN_PAYLOAD_LEN: usize = 6;
+
+    /// Maximum length of a [Ipv6RawExtHeader] the payload
+    pub const MAX_PAYLOAD_LEN: usize = 0xff * 8 + 6;
+
+    /// Returns true if the given header type ip number can be represented in an `Ipv6ExtensionHeader`.
+    pub fn header_type_supported(next_header: IpNumber) -> bool {
+        use crate::ip_number::*;
+        matches!(
+            next_header,
+            IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_DEST_OPTIONS | MOBILITY | HIP | SHIM6
+        )
+    }
+
+    /// Creates an generic IPv6 extension header with the given data.
+    ///
+    /// # Arguments
+    ///
+    /// * `next_header` - type of content after this header (protocol number)
+    /// * `payload` - slice containing the data of the header. This must NOT contain the `next header` and `extended header length` fields of the header.
+    ///
+    /// Note that `payload` must have at least the length of 6 bytes and only supports
+    /// length increases in steps of 8. This measn that the following expression must be true `(payload.len() + 2) % 8 == 0`.
+    /// The maximum length of the payload is `2046` bytes ([`Ipv6RawExtHeader::MAX_PAYLOAD_LEN`]).
+    ///
+    /// If a payload with a non supported length is provided a [`crate::err::ipv6_exts::ExtPayloadLenError`] is returned.
+    pub fn new_raw(
+        next_header: IpNumber,
+        payload: &[u8],
+    ) -> Result<Ipv6RawExtHeader, ExtPayloadLenError> {
+        use ExtPayloadLenError::*;
+        if payload.len() < Self::MIN_PAYLOAD_LEN {
+            Err(TooSmall(payload.len()))
+        } else if payload.len() > Self::MAX_PAYLOAD_LEN {
+            Err(TooBig(payload.len()))
+        } else if 0 != (payload.len() + 2) % 8 {
+            Err(Unaligned(payload.len()))
+        } else {
+            let mut result = Ipv6RawExtHeader {
+                next_header,
+                header_length: ((payload.len() - 6) / 8) as u8,
+                payload_buffer: [0; Self::MAX_PAYLOAD_LEN],
+            };
+            result.payload_buffer[..payload.len()].copy_from_slice(payload);
+            Ok(result)
+        }
+    }
+
+    /// Read an Ipv6ExtensionHeader from a slice and return the header & unused parts of the slice.
+    pub fn from_slice(slice: &[u8]) -> Result<(Ipv6RawExtHeader, &[u8]), err::LenError> {
+        let s = Ipv6RawExtHeaderSlice::from_slice(slice)?;
+        let rest = &slice[s.slice().len()..];
+        let header = s.to_header();
+        Ok((header, rest))
+    }
+
+    /// Return a slice containing the current payload. This does NOT contain
+    /// the `next_header` and `header_length` fields. But everything after these
+    /// two fields.
+    pub fn payload(&self) -> &[u8] {
+        &self.payload_buffer[..(6 + usize::from(self.header_length) * 8)]
+    }
+
+    /// Sets the payload (content of the header after the `next_header` & `header_length` fields).
+    ///
+    /// Note that `payload` must have at least the length of 6 bytes and only supports
+    /// length increases in steps of 8. This measn that the following expression must be true `(payload.len() + 2) % 8 == 0`.
+    /// The maximum length of the payload is `2046` bytes ([`crate::Ipv6RawExtHeader::MAX_PAYLOAD_LEN`]).
+    ///
+    /// If a payload with a non supported length is provided a [`crate::err::ipv6_exts::ExtPayloadLenError`] is returned and the payload of the header is not changed.
+    pub fn set_payload(&mut self, payload: &[u8]) -> Result<(), ExtPayloadLenError> {
+        use ExtPayloadLenError::*;
+        if payload.len() < Self::MIN_PAYLOAD_LEN {
+            Err(TooSmall(payload.len()))
+        } else if payload.len() > Self::MAX_PAYLOAD_LEN {
+            Err(TooBig(payload.len()))
+        } else if 0 != (payload.len() + 2) % 8 {
+            Err(Unaligned(payload.len()))
+        } else {
+            self.payload_buffer[..payload.len()].copy_from_slice(payload);
+            self.header_length = ((payload.len() - 6) / 8) as u8;
+            Ok(())
+        }
+    }
+
+    /// Read an fragment header from the current reader position.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut T,
+    ) -> Result<Ipv6RawExtHeader, std::io::Error> {
+        let (next_header, header_length) = {
+            let mut d: [u8; 2] = [0; 2];
+            reader.read_exact(&mut d)?;
+            (IpNumber(d[0]), d[1])
+        };
+
+        Ok(Ipv6RawExtHeader {
+            next_header,
+            header_length,
+            payload_buffer: {
+                let mut buffer = [0; 0xff * 8 + 6];
+                reader.read_exact(&mut buffer[..usize::from(header_length) * 8 + 6])?;
+                buffer
+            },
+        })
+    }
+
+    /// Read an fragment header from the current limited reader position.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read_limited<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut crate::io::LimitedReader<T>,
+    ) -> Result<Ipv6RawExtHeader, err::io::LimitedReadError> {
+        // set layer start
+        reader.start_layer(err::Layer::Ipv6ExtHeader);
+
+        // read next & len
+        let (next_header, header_length) = {
+            let mut d: [u8; 2] = [0; 2];
+            reader.read_exact(&mut d)?;
+            (IpNumber(d[0]), d[1])
+        };
+
+        Ok(Ipv6RawExtHeader {
+            next_header,
+            header_length,
+            payload_buffer: {
+                let mut buffer = [0; 0xff * 8 + 6];
+                reader.read_exact(&mut buffer[..usize::from(header_length) * 8 + 6])?;
+                buffer
+            },
+        })
+    }
+
+    /// Writes a given IPv6 extension header to the current position.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn write<W: std::io::Write + Sized>(&self, writer: &mut W) -> Result<(), std::io::Error> {
+        writer.write_all(&[self.next_header.0, self.header_length])?;
+        writer.write_all(self.payload())?;
+        Ok(())
+    }
+
+    /// Returns the serialized header.
+    pub fn to_bytes(&self) -> ArrayVec<u8, { Ipv6RawExtHeader::MAX_LEN }> {
+        let mut result = ArrayVec::new();
+        result.extend([self.next_header.0, self.header_length]);
+        // Unwrap Panic Safety:
+        // The following unwrap should never panic, as
+        // the payload length can at most have the size max
+        // header length - 2 and as the internal buffer used to
+        // store the payload data has exactly this size.
+        result.try_extend_from_slice(self.payload()).unwrap();
+        result
+    }
+
+    /// Length of the header in bytes.
+    pub fn header_len(&self) -> usize {
+        2 + (6 + usize::from(self.header_length) * 8)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+    use std::io::Cursor;
+
+    #[test]
+    fn default() {
+        let default_header = Ipv6RawExtHeader {
+            ..Default::default()
+        };
+
+        assert_eq!(default_header.next_header, IpNumber(255));
+        assert_eq!(default_header.header_length, 0);
+        assert_eq!(default_header.payload_buffer, [0; 0xff * 8 + 6])
+    }
+
+    proptest! {
+        #[test]
+        fn debug(header in ipv6_raw_ext_any()) {
+            assert_eq!(
+                format!("{:?}", header),
+                format!(
+                    "Ipv6RawExtHeader {{ next_header: {:?}, payload: {:?} }}",
+                    header.next_header,
+                    header.payload()
+                )
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(header in ipv6_raw_ext_any()) {
+            assert_eq!(header.clone(), header);
+        }
+    }
+
+    #[test]
+    fn header_type_supported() {
+        use ip_number::*;
+        for value in 0..=u8::MAX {
+            let expected_supported = match IpNumber(value) {
+                IPV6_HOP_BY_HOP | IPV6_DEST_OPTIONS | IPV6_ROUTE | MOBILITY | HIP | SHIM6 => true,
+                _ => false,
+            };
+            assert_eq!(
+                expected_supported,
+                Ipv6RawExtHeader::header_type_supported(IpNumber(value))
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn new_raw(header in ipv6_raw_ext_any()) {
+            use ExtPayloadLenError::*;
+
+            // ok
+            {
+                let actual = Ipv6RawExtHeader::new_raw(header.next_header, header.payload()).unwrap();
+                assert_eq!(actual.next_header, header.next_header);
+                assert_eq!(actual.payload(), header.payload());
+            }
+
+            // smaller then minimum
+            for len in 0..Ipv6RawExtHeader::MIN_PAYLOAD_LEN {
+                assert_eq!(
+                    Ipv6RawExtHeader::new_raw(header.next_header, &header.payload()[..len]).unwrap_err(),
+                    TooSmall(len)
+                );
+            }
+
+            // bigger then maximum
+            {
+                let bytes = [0u8;Ipv6RawExtHeader::MAX_PAYLOAD_LEN + 1];
+                assert_eq!(
+                    Ipv6RawExtHeader::new_raw(header.next_header, &bytes).unwrap_err(),
+                    TooBig(bytes.len())
+                );
+            }
+
+            // non aligned payload
+            {
+                let mut bytes = header.to_bytes();
+                bytes.pop().unwrap();
+                bytes.pop().unwrap();
+
+                for offset in 1..8 {
+                    if offset + header.header_len() < Ipv6RawExtHeader::MAX_LEN {
+                        bytes.push(0);
+                        assert_eq!(
+                            Ipv6RawExtHeader::new_raw(header.next_header, &bytes).unwrap_err(),
+                            Unaligned(bytes.len())
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(header in ipv6_raw_ext_any()) {
+            // ok
+            {
+                let mut bytes = Vec::with_capacity(header.header_len() + 2);
+                bytes.extend_from_slice(&header.to_bytes());
+                bytes.push(1);
+                bytes.push(2);
+
+                let (actual_header, actual_rest) = Ipv6RawExtHeader::from_slice(&bytes).unwrap();
+                assert_eq!(actual_header, header);
+                assert_eq!(actual_rest, &[1, 2]);
+            }
+
+            // length error
+            {
+                let bytes = header.to_bytes();
+                for len in 0..bytes.len() {
+                    assert_eq!(
+                        Ipv6RawExtHeader::from_slice(&bytes[..len]).unwrap_err(),
+                        err::LenError{
+                            required_len: if len < Ipv6RawExtHeader::MIN_LEN {
+                                Ipv6RawExtHeader::MIN_LEN
+                            } else {
+                                header.header_len()
+                            },
+                            len: len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv6ExtHeader,
+                            layer_start_offset: 0,
+                        }
+                    );
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn set_payload(
+            header_a in ipv6_raw_ext_any(),
+            header_b in ipv6_raw_ext_any()
+        ) {
+            use ExtPayloadLenError::*;
+            // ok
+            {
+                let mut actual = header_a.clone();
+                actual.set_payload(header_b.payload()).unwrap();
+                assert_eq!(actual.payload(), header_b.payload());
+            }
+
+            // smaller then minimum
+            for len in 0..Ipv6RawExtHeader::MIN_PAYLOAD_LEN {
+                let mut actual = header_a.clone();
+                assert_eq!(
+                    actual.set_payload(&header_b.payload()[..len]).unwrap_err(),
+                    TooSmall(len)
+                );
+                assert_eq!(actual.payload(), header_a.payload());
+            }
+
+            // bigger then maximum
+            {
+                let bytes = [0u8;Ipv6RawExtHeader::MAX_PAYLOAD_LEN + 1];
+                let mut actual = header_a.clone();
+                assert_eq!(
+                    actual.set_payload(&bytes).unwrap_err(),
+                    TooBig(bytes.len())
+                );
+            }
+
+            // non aligned payload
+            {
+                let mut bytes = header_b.to_bytes();
+                bytes.pop().unwrap();
+                bytes.pop().unwrap();
+
+                for offset in 1..8 {
+                    if offset + header_b.header_len() < Ipv6RawExtHeader::MAX_LEN {
+                        bytes.push(0);
+                        let mut actual = header_a.clone();
+                        assert_eq!(
+                            actual.set_payload(&bytes).unwrap_err(),
+                            Unaligned(bytes.len())
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read(header in ipv6_raw_ext_any()) {
+            // ok
+            {
+                let bytes = header.to_bytes();
+                let mut cursor = Cursor::new(&bytes[..]);
+                let actual = Ipv6RawExtHeader::read(&mut cursor).unwrap();
+                assert_eq!(actual, header);
+            }
+
+            // length error
+            {
+                let bytes = header.to_bytes();
+                for len in 0..bytes.len() {
+                    let mut cursor = Cursor::new(&bytes[..len]);
+                    assert!(Ipv6RawExtHeader::read(&mut cursor).is_err());
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write(header in ipv6_raw_ext_any()) {
+            // ok case
+            {
+                let mut buffer = [0u8;Ipv6RawExtHeader::MAX_LEN];
+                let len = {
+                    let mut cursor = Cursor::new(&mut buffer[..]);
+                    header.write(&mut cursor).unwrap();
+                    cursor.position() as usize
+                };
+                let (dec_header, dec_rest) = Ipv6RawExtHeader::from_slice(&buffer[..len]).unwrap();
+                assert_eq!(header, dec_header);
+                assert_eq!(dec_rest, &[]);
+            }
+
+            // length error
+            for len in 0..header.header_len() {
+                let mut buffer = [0u8;Ipv6RawExtHeader::MAX_LEN];
+                let mut cursor = Cursor::new(&mut buffer[..len]);
+                assert!(header.write(&mut cursor).is_err());
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_bytes(header in ipv6_raw_ext_any()) {
+            let bytes = header.to_bytes();
+            assert_eq!(bytes[0], header.next_header.0);
+            assert_eq!(bytes[1], header.header_length);
+            assert_eq!(&bytes[2..], header.payload());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header_len(header in ipv6_raw_ext_any()) {
+            assert_eq!(header.header_len(), header.to_bytes().len());
+        }
+    }
+}
diff --git a/src/net/ipv6_raw_ext_header_slice.rs b/src/net/ipv6_raw_ext_header_slice.rs
new file mode 100644
index 0000000..36b989e
--- /dev/null
+++ b/src/net/ipv6_raw_ext_header_slice.rs
@@ -0,0 +1,234 @@
+use crate::*;
+use core::slice::from_raw_parts;
+
+/// Deprecated. Use [Ipv6RawExtHeaderSlice] instead.
+#[deprecated(
+    since = "0.14.0",
+    note = "Please use the type Ipv6RawExtHeaderSlice instead"
+)]
+pub type Ipv6RawExtensionHeaderSlice<'a> = Ipv6RawExtHeaderSlice<'a>;
+
+/// Slice containing an IPv6 extension header without specific decoding methods (fallback in case no specific implementation is available).
+///
+/// Slice containing an IPv6 extension header with only minimal data interpretation. NOTE only ipv6 header
+/// extensions with the first two bytes representing the next header and the header length
+/// in 8-octets (- 8 octets) can be represented with this struct. This excludes the "Authentication
+/// Header" (AH) and "Encapsulating Security Payload" (ESP).
+///
+/// The following headers can be represented in a Ipv6ExtensionHeaderSlice:
+/// * HopbyHop
+/// * Destination Options
+/// * Routing
+/// * Mobility
+/// * Host Identity Protocol
+/// * Shim6 Protocol
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Ipv6RawExtHeaderSlice<'a> {
+    /// Slice containing the packet data.
+    slice: &'a [u8],
+}
+
+impl<'a> Ipv6RawExtHeaderSlice<'a> {
+    /// Returns true if the given header type ip number can be represented in an `Ipv6ExtensionHeaderSlice`.
+    pub fn header_type_supported(next_header: IpNumber) -> bool {
+        Ipv6RawExtHeader::header_type_supported(next_header)
+    }
+
+    /// Creates a generic ipv6 extension header slice from a slice.
+    pub fn from_slice(slice: &'a [u8]) -> Result<Ipv6RawExtHeaderSlice<'a>, err::LenError> {
+        //check length
+        if slice.len() < 8 {
+            return Err(err::LenError {
+                required_len: 8,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::Ipv6ExtHeader,
+                layer_start_offset: 0,
+            });
+        }
+
+        //check length
+        let len = ((slice[1] as usize) + 1) * 8;
+
+        //check the length again now that the expected length is known
+        if slice.len() < len {
+            return Err(err::LenError {
+                required_len: len,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::Ipv6ExtHeader,
+                layer_start_offset: 0,
+            });
+        }
+
+        //all good
+        Ok(Ipv6RawExtHeaderSlice {
+            // SAFETY:
+            // Safe as the slice has been checked in the previous if
+            // to have at least the the length of the variable len.
+            slice: unsafe { from_raw_parts(slice.as_ptr(), len) },
+        })
+    }
+
+    /// Creates a raw ipv6 extension header slice from a slice (assumes slice
+    /// size & content was validated before).
+    ///
+    /// # Safety
+    ///
+    /// This method assumes that the slice was previously validated to contain
+    /// a valid & supported raw ipv6 extension header. This means the slice length
+    /// must at least be at least 8 and `(slice[1] + 1)*8`. The data that the
+    /// slice points must also be valid (meaning no nullptr or alike allowed).
+    ///
+    /// If these preconditions are not fulfilled the behavior of this function
+    /// and the methods of the return [`IpAuthHeaderSlice`] will be undefined.
+    pub unsafe fn from_slice_unchecked(slice: &'a [u8]) -> Ipv6RawExtHeaderSlice<'a> {
+        Ipv6RawExtHeaderSlice {
+            slice: from_raw_parts(slice.as_ptr(), ((*slice.get_unchecked(1) as usize) + 1) * 8),
+        }
+    }
+
+    /// Returns the slice containing the ipv6 extension header
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+
+    /// Returns the IP protocol number of the next header or transport layer protocol.
+    ///
+    /// See [IpNumber] or [ip_number] for a definition of the known values.
+    #[inline]
+    pub fn next_header(&self) -> IpNumber {
+        IpNumber(unsafe { *self.slice.get_unchecked(0) })
+    }
+
+    /// Returns a slice containing the payload data of the header.
+    ///
+    /// This contains all the data after the header length field
+    /// until the end of the header (length specified by the
+    /// hdr ext length field).
+    #[inline]
+    pub fn payload(&self) -> &'a [u8] {
+        unsafe { from_raw_parts(self.slice.as_ptr().add(2), self.slice.len() - 2) }
+    }
+
+    /// Convert the slice to an [Ipv6RawExtHeader].
+    ///
+    /// Decode some of the fields and copy the results to a
+    /// [Ipv6RawExtHeader] struct together with a slice pointing
+    /// to the non decoded parts.
+    pub fn to_header(&self) -> Ipv6RawExtHeader {
+        Ipv6RawExtHeader::new_raw(self.next_header(), self.payload()).unwrap()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{test_gens::*, *};
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn debug(header in ipv6_raw_ext_any()) {
+            let bytes = header.to_bytes();
+            let slice = Ipv6RawExtHeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(
+                format!("{:?}", slice),
+                format!("Ipv6RawExtHeaderSlice {{ slice: {:?} }}", slice.slice())
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(header in ipv6_raw_ext_any()) {
+            let bytes = header.to_bytes();
+            let slice = Ipv6RawExtHeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(slice.clone(), slice);
+        }
+    }
+
+    #[test]
+    fn header_type_supported() {
+        use ip_number::*;
+        for value in 0..=u8::MAX {
+            let expected_supported = match IpNumber(value) {
+                IPV6_HOP_BY_HOP | IPV6_DEST_OPTIONS | IPV6_ROUTE | MOBILITY | HIP | SHIM6 => true,
+                _ => false,
+            };
+            assert_eq!(
+                expected_supported,
+                Ipv6RawExtHeaderSlice::header_type_supported(IpNumber(value))
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(header in ipv6_raw_ext_any()) {
+            // ok
+            {
+                let mut bytes = Vec::with_capacity(header.header_len() + 2);
+                bytes.extend_from_slice(&header.to_bytes());
+                bytes.push(1);
+                bytes.push(2);
+
+                let (actual_header, actual_rest) = Ipv6RawExtHeader::from_slice(&bytes).unwrap();
+                assert_eq!(actual_header, header);
+                assert_eq!(actual_rest, &[1, 2]);
+            }
+
+            // length error
+            {
+                let bytes = header.to_bytes();
+                for len in 0..bytes.len() {
+                    assert_eq!(
+                        Ipv6RawExtHeader::from_slice(&bytes[..len]).unwrap_err(),
+                        err::LenError{
+                            required_len: if len < Ipv6RawExtHeader::MIN_LEN {
+                                Ipv6RawExtHeader::MIN_LEN
+                            } else {
+                                header.header_len()
+                            },
+                            len: len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv6ExtHeader,
+                            layer_start_offset: 0,
+                        }
+                    );
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice_unchecked(header in ipv6_raw_ext_any()) {
+            let bytes = header.to_bytes();
+            let slice = unsafe {
+                Ipv6RawExtHeaderSlice::from_slice_unchecked(&bytes)
+            };
+            assert_eq!(&bytes[..], slice.slice());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn getters(header in ipv6_raw_ext_any()) {
+            let bytes = header.to_bytes();
+            let slice = Ipv6RawExtHeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(slice.next_header(), header.next_header);
+            assert_eq!(slice.payload(), header.payload());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_header(header in ipv6_raw_ext_any()) {
+            let bytes = header.to_bytes();
+            let slice = Ipv6RawExtHeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(header, slice.to_header());
+        }
+    }
+}
diff --git a/src/net/ipv6_routing_exts.rs b/src/net/ipv6_routing_exts.rs
new file mode 100644
index 0000000..b3b4aa9
--- /dev/null
+++ b/src/net/ipv6_routing_exts.rs
@@ -0,0 +1,86 @@
+use crate::*;
+
+/// In case a route header is present it is also possible
+/// to attach a "final destination" header.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Ipv6RoutingExtensions {
+    pub routing: Ipv6RawExtHeader,
+    pub final_destination_options: Option<Ipv6RawExtHeader>,
+}
+
+impl Ipv6RoutingExtensions {
+    /// Minimum length required for routing extension headers in bytes/octets.
+    pub const MIN_LEN: usize = Ipv6RawExtHeader::MAX_LEN;
+
+    /// Maximum summed up length of all extension headers in bytes/octets.
+    pub const MAX_LEN: usize = Ipv6RawExtHeader::MAX_LEN * 2;
+
+    /// Return the length of the headers in bytes.
+    pub fn header_len(&self) -> usize {
+        self.routing.header_len()
+            + self
+                .final_destination_options
+                .as_ref()
+                .map(|h| h.header_len())
+                .unwrap_or(0)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::test_gens::ipv6_raw_ext_any;
+    use proptest::prelude::*;
+
+    #[test]
+    fn debug() {
+        use alloc::format;
+
+        let a: Ipv6RoutingExtensions = Ipv6RoutingExtensions {
+            routing: Ipv6RawExtHeader::new_raw(0.into(), &[0; 6]).unwrap(),
+            final_destination_options: None,
+        };
+        assert_eq!(
+            &format!(
+                "Ipv6RoutingExtensions {{ routing: {:?}, final_destination_options: {:?} }}",
+                a.routing, a.final_destination_options,
+            ),
+            &format!("{:?}", a)
+        );
+    }
+
+    #[test]
+    fn clone_eq() {
+        let a: Ipv6RoutingExtensions = Ipv6RoutingExtensions {
+            routing: Ipv6RawExtHeader::new_raw(0.into(), &[0; 6]).unwrap(),
+            final_destination_options: None,
+        };
+        assert_eq!(a, a.clone());
+    }
+
+    proptest! {
+        #[test]
+        fn header_len(
+            routing in ipv6_raw_ext_any(),
+            final_destination_options in ipv6_raw_ext_any()
+        ) {
+            // without final dest options
+            assert_eq!(
+                Ipv6RoutingExtensions{
+                    routing: routing.clone(),
+                    final_destination_options: None,
+                }.header_len(),
+                routing.header_len()
+            );
+
+            // with final dest options
+            assert_eq!(
+                Ipv6RoutingExtensions{
+                    routing: routing.clone(),
+                    final_destination_options: Some(final_destination_options.clone()),
+                }.header_len(),
+                routing.header_len() + final_destination_options.header_len()
+            );
+        }
+    }
+}
diff --git a/src/net/ipv6_slice.rs b/src/net/ipv6_slice.rs
new file mode 100644
index 0000000..13d431f
--- /dev/null
+++ b/src/net/ipv6_slice.rs
@@ -0,0 +1,813 @@
+use crate::err::{ipv6::SliceError, Layer, LenError};
+use crate::*;
+
+/// Slice containing the IPv6 headers & payload.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Ipv6Slice<'a> {
+    pub(crate) header: Ipv6HeaderSlice<'a>,
+    pub(crate) exts: Ipv6ExtensionsSlice<'a>,
+    pub(crate) payload: IpPayloadSlice<'a>,
+}
+
+impl<'a> Ipv6Slice<'a> {
+    /// Separates and validates IPv6 headers (including extension headers)
+    /// in the given slice and determine the sub-slice containing the payload
+    /// of the IPv6 packet (based on the payload length value in the header).
+    ///
+    /// Note that his function returns an [`crate::err::LenError`] if the given slice
+    /// contains less data then the `payload_len` field in the IPv6 header indicates
+    /// should be present.
+    ///
+    /// If you want to ignore these kind of length errors based on the length
+    /// fields in the IP headers use [`Ipv6Slice::from_slice_lax`] instead.
+    pub fn from_slice(slice: &'a [u8]) -> Result<Ipv6Slice<'a>, SliceError> {
+        // try reading the header
+        let header = Ipv6HeaderSlice::from_slice(slice).map_err(|err| {
+            use crate::err::ipv6::HeaderSliceError::*;
+            match err {
+                Len(err) => SliceError::Len(err),
+                Content(err) => SliceError::Header(err),
+            }
+        })?;
+
+        // restrict slice by the length specified in the header
+        let (header_payload, len_source) =
+            if 0 == header.payload_length() && slice.len() > Ipv6Header::LEN {
+                // In case the payload_length is 0 assume that the entire
+                // rest of the slice is part of the packet until the jumbogram
+                // parameters can be parsed.
+
+                // TODO: Add payload length parsing from the jumbogram
+                (
+                    unsafe {
+                        core::slice::from_raw_parts(
+                            slice.as_ptr().add(Ipv6Header::LEN),
+                            slice.len() - Ipv6Header::LEN,
+                        )
+                    },
+                    LenSource::Slice,
+                )
+            } else {
+                let payload_len = usize::from(header.payload_length());
+                let expected_len = Ipv6Header::LEN + payload_len;
+                if slice.len() < expected_len {
+                    return Err(SliceError::Len(LenError {
+                        required_len: expected_len,
+                        len: slice.len(),
+                        len_source: LenSource::Slice,
+                        layer: Layer::Ipv6Packet,
+                        layer_start_offset: 0,
+                    }));
+                } else {
+                    (
+                        unsafe {
+                            core::slice::from_raw_parts(
+                                slice.as_ptr().add(Ipv6Header::LEN),
+                                payload_len,
+                            )
+                        },
+                        LenSource::Ipv6HeaderPayloadLen,
+                    )
+                }
+            };
+
+        // parse extension headers
+        let (exts, payload_ip_number, payload) =
+            Ipv6ExtensionsSlice::from_slice(header.next_header(), header_payload).map_err(
+                |err| {
+                    // modify length errors
+                    use crate::err::ipv6_exts::HeaderSliceError::*;
+                    match err {
+                        Len(mut err) => {
+                            err.len_source = LenSource::Ipv6HeaderPayloadLen;
+                            err.layer_start_offset += Ipv6Header::LEN;
+                            SliceError::Len(err)
+                        }
+                        Content(err) => SliceError::Exts(err),
+                    }
+                },
+            )?;
+
+        let fragmented = exts.is_fragmenting_payload();
+        Ok(Ipv6Slice {
+            header,
+            exts,
+            payload: IpPayloadSlice {
+                ip_number: payload_ip_number,
+                fragmented,
+                len_source,
+                payload,
+            },
+        })
+    }
+
+    /// Seperate 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:
+    ///
+    /// * 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.
+    /// * Parsing packets where the `payload_length` (in the IPv6 header) has not
+    ///   yet been set. This can be useful when parsing packets which have been
+    ///   recorded in a layer before the length field was set (e.g. before the operating
+    ///   system set the length fields).
+    ///
+    /// # Differences to `from_slice`:
+    ///
+    /// The main differences is that the function ignores inconsistent
+    /// `payload_length` values (in IPv6 headers). When these length values
+    /// in the IP header are inconsistant 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
+    /// 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`
+    /// field in the IPv6 header is
+    ///
+    /// * Bigger then the given slice (payload cannot fully be seperated).
+    /// * The value `0`.
+    pub fn from_slice_lax(slice: &'a [u8]) -> Result<Ipv6Slice<'a>, SliceError> {
+        // try reading the header
+        let header = Ipv6HeaderSlice::from_slice(slice).map_err(|err| {
+            use crate::err::ipv6::HeaderSliceError::*;
+            match err {
+                Len(err) => SliceError::Len(err),
+                Content(err) => SliceError::Header(err),
+            }
+        })?;
+
+        // restrict slice by the length specified in the header
+        let (header_payload, len_source) =
+            if 0 == header.payload_length() && slice.len() > Ipv6Header::LEN {
+                // In case the payload_length is 0 assume that the entire
+                // rest of the slice is part of the packet until the jumbogram
+                // parameters can be parsed.
+
+                // TODO: Add payload length parsing from the jumbogram
+                (
+                    unsafe {
+                        core::slice::from_raw_parts(
+                            slice.as_ptr().add(Ipv6Header::LEN),
+                            slice.len() - Ipv6Header::LEN,
+                        )
+                    },
+                    LenSource::Slice,
+                )
+            } else {
+                let payload_len = usize::from(header.payload_length());
+                let expected_len = Ipv6Header::LEN + payload_len;
+                if slice.len() < expected_len {
+                    (
+                        unsafe {
+                            core::slice::from_raw_parts(
+                                slice.as_ptr().add(Ipv6Header::LEN),
+                                slice.len() - Ipv6Header::LEN,
+                            )
+                        },
+                        LenSource::Slice,
+                    )
+                } else {
+                    (
+                        unsafe {
+                            core::slice::from_raw_parts(
+                                slice.as_ptr().add(Ipv6Header::LEN),
+                                payload_len,
+                            )
+                        },
+                        LenSource::Ipv6HeaderPayloadLen,
+                    )
+                }
+            };
+
+        // parse extension headers
+        let (exts, payload_ip_number, payload) =
+            Ipv6ExtensionsSlice::from_slice(header.next_header(), header_payload).map_err(
+                |err| {
+                    // modify length errors
+                    use crate::err::ipv6_exts::HeaderSliceError::*;
+                    match err {
+                        Len(mut err) => {
+                            err.len_source = len_source;
+                            err.layer_start_offset += Ipv6Header::LEN;
+                            SliceError::Len(err)
+                        }
+                        Content(err) => SliceError::Exts(err),
+                    }
+                },
+            )?;
+
+        let fragmented = exts.is_fragmenting_payload();
+        Ok(Ipv6Slice {
+            header,
+            exts,
+            payload: IpPayloadSlice {
+                ip_number: payload_ip_number,
+                fragmented,
+                len_source,
+                payload,
+            },
+        })
+    }
+
+    /// Returns a slice containing the IPv6 header.
+    #[inline]
+    pub fn header(&self) -> Ipv6HeaderSlice<'a> {
+        self.header
+    }
+
+    /// Returns a slice containing the IPv6 extension headers.
+    #[inline]
+    pub fn extensions(&self) -> &Ipv6ExtensionsSlice<'a> {
+        &self.exts
+    }
+
+    /// Returns a slice containing the data after the IPv6 header
+    /// and IPv6 extensions headers.
+    #[inline]
+    pub fn payload(&self) -> &IpPayloadSlice<'a> {
+        &self.payload
+    }
+
+    /// Returns true if the payload is flagged as being fragmented.
+    #[inline]
+    pub fn is_payload_fragmented(&self) -> bool {
+        self.payload.fragmented
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::{
+        ip_number::{AUTH, IGMP, UDP},
+        test_gens::*,
+    };
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn debug_clone_eq(
+            ipv6_base in ipv6_any(),
+            auth_base in ip_auth_any()
+        ) {
+            let mut auth = auth_base.clone();
+            auth.next_header = IGMP;
+            let payload: [u8;4] = [1,2,3,4];
+            let mut data = Vec::with_capacity(
+                ipv6_base.header_len() +
+                auth.header_len() +
+                payload.len()
+            );
+            let mut ipv6 = ipv6_base.clone();
+            ipv6.next_header = AUTH;
+            ipv6.payload_length = (auth.header_len() + payload.len()) as u16;
+            data.extend_from_slice(&ipv6.to_bytes());
+            data.extend_from_slice(&auth.to_bytes());
+            data.extend_from_slice(&payload);
+
+            // decode packet
+            let slice = Ipv6Slice::from_slice(&data).unwrap();
+
+            // check debug output
+            prop_assert_eq!(
+                format!("{:?}", slice),
+                format!(
+                    "Ipv6Slice {{ header: {:?}, exts: {:?}, payload: {:?} }}",
+                    slice.header(),
+                    slice.extensions(),
+                    slice.payload()
+                )
+            );
+            prop_assert_eq!(slice.clone(), slice);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            ipv6_base in ipv6_any(),
+            auth_base in ip_auth_any()
+        ) {
+            let payload: [u8;6] = [1,2,3,4,5,6];
+
+            // build packets
+            let data_without_ext = {
+                let mut data = Vec::with_capacity(
+                    ipv6_base.header_len() +
+                    payload.len() +
+                    4
+                );
+                let mut ipv6 = ipv6_base.clone();
+                ipv6.payload_length = (payload.len()) as u16;
+                ipv6.next_header = UDP;
+                data.extend_from_slice(&ipv6.to_bytes());
+                data.extend_from_slice(&payload);
+                data.extend_from_slice(&[0,0,0,0]);
+                data
+            };
+            let data_with_ext = {
+                let payload: [u8;6] = [1,2,3,4,5,6];
+                let mut data = Vec::with_capacity(
+                    ipv6_base.header_len() +
+                    auth_base.header_len() +
+                    payload.len() +
+                    4
+                );
+                let mut ipv6 = ipv6_base.clone();
+                ipv6.payload_length = (auth_base.header_len() + payload.len()) as u16;
+                ipv6.next_header = AUTH;
+                let mut auth = auth_base.clone();
+                auth.next_header = UDP;
+                data.extend_from_slice(&ipv6.to_bytes());
+                data.extend_from_slice(&auth.to_bytes());
+                data.extend_from_slice(&payload);
+                data.extend_from_slice(&[0,0,0,0]);
+                data
+            };
+
+            // parsing without extensions (normal length)
+            {
+                let actual = Ipv6Slice::from_slice(&data_without_ext).unwrap();
+                prop_assert_eq!(actual.header().slice(), &data_without_ext[..ipv6_base.header_len()]);
+                prop_assert!(actual.extensions().first_header().is_none());
+                prop_assert_eq!(
+                    actual.payload(),
+                    &IpPayloadSlice{
+                        ip_number: UDP.into(),
+                        fragmented: false,
+                        len_source: LenSource::Ipv6HeaderPayloadLen,
+                        payload: &payload,
+                    }
+                );
+            }
+
+            // parsing with extensions (normal length)
+            {
+                let actual = Ipv6Slice::from_slice(&data_with_ext).unwrap();
+                prop_assert_eq!(actual.header().slice(), &data_with_ext[..ipv6_base.header_len()]);
+                let (expected, _, _) = Ipv6ExtensionsSlice::from_slice(AUTH, &data_with_ext[ipv6_base.header_len()..]).unwrap();
+                prop_assert_eq!(
+                    actual.extensions(),
+                    &expected
+                );
+                prop_assert_eq!(
+                    actual.payload(),
+                    &IpPayloadSlice{
+                        ip_number: UDP.into(),
+                        fragmented: false,
+                        len_source: LenSource::Ipv6HeaderPayloadLen,
+                        payload: &payload,
+                    }
+                );
+            }
+
+            // parsing without extensions (zero length, fallback to slice length)
+            {
+                // inject zero as payload length
+                let mut data = data_without_ext.clone();
+                data[4] = 0;
+                data[5] = 0;
+                let actual = Ipv6Slice::from_slice(&data).unwrap();
+                prop_assert_eq!(actual.header().slice(), &data[..ipv6_base.header_len()]);
+                prop_assert!(actual.extensions().first_header().is_none());
+                prop_assert_eq!(
+                    actual.payload(),
+                    &IpPayloadSlice{
+                        ip_number: UDP.into(),
+                        fragmented: false,
+                        len_source: LenSource::Slice,
+                        payload: &data[ipv6_base.header_len()..],
+                    }
+                );
+            }
+
+            // parsing with extensions (zero length, fallback to slice length)
+            {
+                // inject zero as payload length
+                let mut data = data_with_ext.clone();
+                data[4] = 0;
+                data[5] = 0;
+                let actual = Ipv6Slice::from_slice(&data).unwrap();
+                prop_assert_eq!(actual.header().slice(), &data[..ipv6_base.header_len()]);
+                let (expected, _, _) = Ipv6ExtensionsSlice::from_slice(AUTH, &data[ipv6_base.header_len()..]).unwrap();
+                prop_assert_eq!(
+                    actual.extensions(),
+                    &expected
+                );
+                prop_assert_eq!(
+                    actual.payload(),
+                    &IpPayloadSlice{
+                        ip_number: UDP.into(),
+                        fragmented: false,
+                        len_source: LenSource::Slice,
+                        payload: &data[ipv6_base.header_len() + auth_base.header_len()..],
+                    }
+                );
+            }
+
+            // header content error
+            {
+                use crate::err::ipv6::HeaderError;
+                // inject invalid ip version
+                let mut data = data_without_ext.clone();
+                data[0] = data[0] & 0x0f; // version 0
+                prop_assert_eq!(
+                    Ipv6Slice::from_slice(&data).unwrap_err(),
+                    SliceError::Header(
+                        HeaderError::UnexpectedVersion{ version_number: 0 }
+                    )
+                );
+            }
+
+            // header length error
+            for len in 0..Ipv6Header::LEN {
+                prop_assert_eq!(
+                    Ipv6Slice::from_slice(&data_without_ext[..len]).unwrap_err(),
+                    SliceError::Len(
+                        LenError{
+                            required_len: Ipv6Header::LEN,
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: Layer::Ipv6Header,
+                            layer_start_offset: 0
+                        }
+                    )
+                );
+            }
+
+            // payload length error without auth header
+            {
+                use crate::err::{LenError, Layer};
+
+                let required_len = ipv6_base.header_len() + payload.len();
+                prop_assert_eq!(
+                    Ipv6Slice::from_slice(&data_without_ext[..required_len - 1]).unwrap_err(),
+                    SliceError::Len(LenError{
+                        required_len: required_len,
+                        len: required_len - 1,
+                        len_source: LenSource::Slice,
+                        layer: Layer::Ipv6Packet,
+                        layer_start_offset: 0,
+                    })
+                );
+            }
+
+            // payload length error auth header
+            {
+                use crate::err::{LenError, Layer};
+
+                let required_len = ipv6_base.header_len() + auth_base.header_len() + payload.len();
+                prop_assert_eq!(
+                    Ipv6Slice::from_slice(&data_with_ext[..required_len - 1]).unwrap_err(),
+                    SliceError::Len(LenError{
+                        required_len: required_len,
+                        len: required_len - 1,
+                        len_source: LenSource::Slice,
+                        layer: Layer::Ipv6Packet,
+                        layer_start_offset: 0,
+                    })
+                );
+            }
+
+            // auth length error
+            {
+                use crate::err::{LenError, Layer};
+
+                // inject payload length that is smaller then the auth header
+                let mut data = data_with_ext.clone();
+                let payload_len_too_small = auth_base.header_len() - 1;
+                {
+                    let plts = (payload_len_too_small as u16).to_be_bytes();
+                    data[4] = plts[0];
+                    data[5] = plts[1];
+                }
+
+                prop_assert_eq!(
+                    Ipv6Slice::from_slice(&data).unwrap_err(),
+                    SliceError::Len(
+                        LenError{
+                            required_len: auth_base.header_len(),
+                            len: auth_base.header_len() - 1,
+                            len_source: LenSource::Ipv6HeaderPayloadLen,
+                            layer: Layer::IpAuthHeader,
+                            layer_start_offset: ipv6_base.header_len(),
+                        }
+                    )
+                );
+            }
+
+            // auth content error
+            {
+                use crate::err::{ip_auth, ipv6_exts};
+
+                // inject zero as auth header length
+                let mut data = data_with_ext.clone();
+                data[ipv6_base.header_len() + 1] = 0;
+
+                prop_assert_eq!(
+                    Ipv6Slice::from_slice(&data).unwrap_err(),
+                    SliceError::Exts(ipv6_exts::HeaderError::IpAuth(
+                        ip_auth::HeaderError::ZeroPayloadLen
+                    ))
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice_lax(
+            ipv6_base in ipv6_any(),
+            auth_base in ip_auth_any()
+        ) {
+            let payload: [u8;6] = [1,2,3,4,5,6];
+
+            // build packets
+            let data_without_ext = {
+                let mut data = Vec::with_capacity(
+                    ipv6_base.header_len() +
+                    payload.len() +
+                    4
+                );
+                let mut ipv6 = ipv6_base.clone();
+                ipv6.payload_length = (payload.len()) as u16;
+                ipv6.next_header = UDP;
+                data.extend_from_slice(&ipv6.to_bytes());
+                data.extend_from_slice(&payload);
+                data.extend_from_slice(&[0,0,0,0]);
+                data
+            };
+            let data_with_ext = {
+                let payload: [u8;6] = [1,2,3,4,5,6];
+                let mut data = Vec::with_capacity(
+                    ipv6_base.header_len() +
+                    auth_base.header_len() +
+                    payload.len() +
+                    4
+                );
+                let mut ipv6 = ipv6_base.clone();
+                ipv6.payload_length = (auth_base.header_len() + payload.len()) as u16;
+                ipv6.next_header = AUTH;
+                let mut auth = auth_base.clone();
+                auth.next_header = UDP;
+                data.extend_from_slice(&ipv6.to_bytes());
+                data.extend_from_slice(&auth.to_bytes());
+                data.extend_from_slice(&payload);
+                data.extend_from_slice(&[0,0,0,0]);
+                data
+            };
+
+            // parsing without extensions (normal length)
+            {
+                let actual = Ipv6Slice::from_slice_lax(&data_without_ext).unwrap();
+                prop_assert_eq!(actual.header().slice(), &data_without_ext[..ipv6_base.header_len()]);
+                prop_assert!(actual.extensions().first_header().is_none());
+                prop_assert_eq!(
+                    actual.payload(),
+                    &IpPayloadSlice{
+                        ip_number: UDP.into(),
+                        fragmented: false,
+                        len_source: LenSource::Ipv6HeaderPayloadLen,
+                        payload: &payload,
+                    }
+                );
+            }
+
+            // parsing with extensions (normal length)
+            {
+                let actual = Ipv6Slice::from_slice_lax(&data_with_ext).unwrap();
+                prop_assert_eq!(actual.header().slice(), &data_with_ext[..ipv6_base.header_len()]);
+                let (expected, _, _) = Ipv6ExtensionsSlice::from_slice(AUTH, &data_with_ext[ipv6_base.header_len()..]).unwrap();
+                prop_assert_eq!(
+                    actual.extensions(),
+                    &expected
+                );
+                prop_assert_eq!(
+                    actual.payload(),
+                    &IpPayloadSlice{
+                        ip_number: UDP.into(),
+                        fragmented: false,
+                        len_source: LenSource::Ipv6HeaderPayloadLen,
+                        payload: &payload,
+                    }
+                );
+            }
+
+            // parsing without extensions (zero length, fallback to slice length)
+            {
+                // inject zero as payload length
+                let mut data = data_without_ext.clone();
+                data[4] = 0;
+                data[5] = 0;
+                let actual = Ipv6Slice::from_slice_lax(&data).unwrap();
+                prop_assert_eq!(actual.header().slice(), &data[..ipv6_base.header_len()]);
+                prop_assert!(actual.extensions().first_header().is_none());
+                prop_assert_eq!(
+                    actual.payload(),
+                    &IpPayloadSlice{
+                        ip_number: UDP.into(),
+                        fragmented: false,
+                        len_source: LenSource::Slice,
+                        payload: &data[ipv6_base.header_len()..],
+                    }
+                );
+            }
+
+            // parsing with extensions (zero length, fallback to slice length)
+            {
+                // inject zero as payload length
+                let mut data = data_with_ext.clone();
+                data[4] = 0;
+                data[5] = 0;
+                let actual = Ipv6Slice::from_slice_lax(&data).unwrap();
+                prop_assert_eq!(actual.header().slice(), &data[..ipv6_base.header_len()]);
+                let (expected, _, _) = Ipv6ExtensionsSlice::from_slice(AUTH, &data[ipv6_base.header_len()..]).unwrap();
+                prop_assert_eq!(
+                    actual.extensions(),
+                    &expected
+                );
+                prop_assert_eq!(
+                    actual.payload(),
+                    &IpPayloadSlice{
+                        ip_number: UDP.into(),
+                        fragmented: false,
+                        len_source: LenSource::Slice,
+                        payload: &data[ipv6_base.header_len() + auth_base.header_len()..],
+                    }
+                );
+            }
+
+            // header content error
+            {
+                use crate::err::ipv6::HeaderError;
+                // inject invalid ip version
+                let mut data = data_without_ext.clone();
+                data[0] = data[0] & 0x0f; // version 0
+                prop_assert_eq!(
+                    Ipv6Slice::from_slice_lax(&data).unwrap_err(),
+                    SliceError::Header(
+                        HeaderError::UnexpectedVersion{ version_number: 0 }
+                    )
+                );
+            }
+
+            // header length error
+            for len in 0..Ipv6Header::LEN {
+                prop_assert_eq!(
+                    Ipv6Slice::from_slice_lax(&data_without_ext[..len]).unwrap_err(),
+                    SliceError::Len(
+                        LenError{
+                            required_len: Ipv6Header::LEN,
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: Layer::Ipv6Header,
+                            layer_start_offset: 0
+                        }
+                    )
+                );
+            }
+
+            // payload length larger then slice (fallback to slice length)
+            {
+                let len = ipv6_base.header_len() + payload.len() - 1;
+                let actual = Ipv6Slice::from_slice_lax(&data_without_ext[..len]).unwrap();
+                prop_assert_eq!(actual.header().slice(), &data_without_ext[..ipv6_base.header_len()]);
+                prop_assert_eq!(
+                    0,
+                    actual.extensions().slice().len()
+                );
+                prop_assert_eq!(
+                    actual.payload(),
+                    &IpPayloadSlice{
+                        ip_number: UDP.into(),
+                        fragmented: false,
+                        len_source: LenSource::Slice,
+                        payload: &data_without_ext[ipv6_base.header_len()..len],
+                    }
+                );
+            }
+
+            // payload length error auth header
+            {
+                use crate::err::{LenError, Layer};
+
+                let required_len = ipv6_base.header_len() + auth_base.header_len();
+                prop_assert_eq!(
+                    Ipv6Slice::from_slice_lax(&data_with_ext[..required_len - 1]).unwrap_err(),
+                    SliceError::Len(LenError{
+                        required_len: required_len - Ipv6Header::LEN,
+                        len: required_len - Ipv6Header::LEN - 1,
+                        len_source: LenSource::Slice,
+                        layer: Layer::IpAuthHeader,
+                        layer_start_offset: Ipv6Header::LEN,
+                    })
+                );
+            }
+
+            // auth length error
+            {
+                use crate::err::{LenError, Layer};
+
+                // inject payload length that is smaller then the auth header
+                let mut data = data_with_ext.clone();
+                let payload_len_too_small = auth_base.header_len() - 1;
+                {
+                    let plts = (payload_len_too_small as u16).to_be_bytes();
+                    data[4] = plts[0];
+                    data[5] = plts[1];
+                }
+
+                prop_assert_eq!(
+                    Ipv6Slice::from_slice_lax(&data).unwrap_err(),
+                    SliceError::Len(
+                        LenError{
+                            required_len: auth_base.header_len(),
+                            len: auth_base.header_len() - 1,
+                            len_source: LenSource::Ipv6HeaderPayloadLen,
+                            layer: Layer::IpAuthHeader,
+                            layer_start_offset: ipv6_base.header_len(),
+                        }
+                    )
+                );
+            }
+
+            // auth content error
+            {
+                use crate::err::{ip_auth, ipv6_exts};
+
+                // inject zero as auth header length
+                let mut data = data_with_ext.clone();
+                data[ipv6_base.header_len() + 1] = 0;
+
+                prop_assert_eq!(
+                    Ipv6Slice::from_slice_lax(&data).unwrap_err(),
+                    SliceError::Exts(ipv6_exts::HeaderError::IpAuth(
+                        ip_auth::HeaderError::ZeroPayloadLen
+                    ))
+                );
+            }
+        }
+    }
+
+    #[test]
+    fn is_payload_fragmented() {
+        use crate::ip_number::{IPV6_FRAG, UDP};
+
+        // not fragmented
+        {
+            let data = Ipv6Header {
+                traffic_class: 0,
+                flow_label: 1.try_into().unwrap(),
+                payload_length: 0,
+                next_header: UDP,
+                hop_limit: 4,
+                source: [0; 16],
+                destination: [0; 16],
+            }
+            .to_bytes();
+            assert_eq!(
+                false,
+                Ipv6Slice::from_slice(&data)
+                    .unwrap()
+                    .is_payload_fragmented()
+            );
+        }
+
+        // fragmented
+        {
+            let ipv6_frag = Ipv6FragmentHeader {
+                next_header: UDP,
+                fragment_offset: 0.try_into().unwrap(),
+                more_fragments: true,
+                identification: 0,
+            };
+            let ipv6 = Ipv6Header {
+                traffic_class: 0,
+                flow_label: 1.try_into().unwrap(),
+                payload_length: ipv6_frag.header_len() as u16,
+                next_header: IPV6_FRAG,
+                hop_limit: 4,
+                source: [0; 16],
+                destination: [0; 16],
+            };
+
+            let mut data = Vec::with_capacity(ipv6.header_len() + ipv6_frag.header_len());
+            data.extend_from_slice(&ipv6.to_bytes());
+            data.extend_from_slice(&ipv6_frag.to_bytes());
+            assert!(Ipv6Slice::from_slice(&data)
+                .unwrap()
+                .is_payload_fragmented());
+        }
+    }
+}
diff --git a/src/net/lax_ip_payload_slice.rs b/src/net/lax_ip_payload_slice.rs
new file mode 100644
index 0000000..350dcea
--- /dev/null
+++ b/src/net/lax_ip_payload_slice.rs
@@ -0,0 +1,91 @@
+use crate::*;
+
+/// Laxly identified payload of an IP packet (potentially incomplete).
+///
+/// To check if the payload is complete check the `incomplete` field.
+#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
+pub struct LaxIpPayloadSlice<'a> {
+    /// True if the length field in the IP header indicates more data
+    /// should be present but it was not (aka the packet data is cut off).
+    ///
+    /// Note that this different from fragmentation. If a packet is
+    /// fragmented the length field in the individual IP headers is
+    /// still correctly set.
+    pub incomplete: bool,
+
+    /// Identifying content of the payload.
+    pub ip_number: IpNumber,
+
+    /// True if the payload is not complete and has been fragmented.
+    ///
+    /// This can occur if the IPv4 incdicates that the payload
+    /// has been fragmented or if there is an IPv6 fragmentation
+    /// header indicating that the payload has been fragmented.
+    pub fragmented: bool,
+
+    /// Length field that was used to determine the length
+    /// of the payload (e.g. IPv6 "payload_length" field).
+    pub len_source: LenSource,
+
+    /// Payload
+    pub payload: &'a [u8],
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        let s = LaxIpPayloadSlice {
+            incomplete: false,
+            ip_number: IpNumber::UDP,
+            fragmented: true,
+            len_source: LenSource::Slice,
+            payload: &[],
+        };
+        assert_eq!(
+            format!(
+                "LaxIpPayloadSlice {{ incomplete: {:?}, ip_number: {:?}, fragmented: {:?}, len_source: {:?}, payload: {:?} }}",
+                s.incomplete,
+                s.ip_number,
+                s.fragmented,
+                s.len_source,
+                s.payload
+            ),
+            format!("{:?}", s)
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash_ord() {
+        let s = LaxIpPayloadSlice {
+            incomplete: false,
+            ip_number: IpNumber::UDP,
+            fragmented: true,
+            len_source: LenSource::Slice,
+            payload: &[],
+        };
+        assert_eq!(s.clone(), s);
+
+        use std::collections::hash_map::DefaultHasher;
+        use std::hash::{Hash, Hasher};
+
+        let a_hash = {
+            let mut hasher = DefaultHasher::new();
+            s.hash(&mut hasher);
+            hasher.finish()
+        };
+        let b_hash = {
+            let mut hasher = DefaultHasher::new();
+            s.clone().hash(&mut hasher);
+            hasher.finish()
+        };
+        assert_eq!(a_hash, b_hash);
+
+        use std::cmp::Ordering;
+        assert_eq!(s.clone().cmp(&s), Ordering::Equal);
+        assert_eq!(s.clone().partial_cmp(&s), Some(Ordering::Equal));
+    }
+}
diff --git a/src/net/lax_ip_slice.rs b/src/net/lax_ip_slice.rs
new file mode 100644
index 0000000..97477dc
--- /dev/null
+++ b/src/net/lax_ip_slice.rs
@@ -0,0 +1,1155 @@
+use crate::{err::*, *};
+
+/// Slice containing laxly separated IPv4 or IPv6 headers & payload.
+///
+/// Compared to the normal [`IpSlice`] this slice allows the
+/// payload to be incomplete/cut off and errors to be present in
+/// the IpPayload.
+///
+/// The main usecases 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.
+/// * Parsing packets where the `total_len` (for IPv4) or `payload_len` (for IPv6)
+///   have not yet been set. This can be useful when parsing packets which have
+///   been recorded in a layer before the length field was set (e.g. before the
+///   operating system set the length fields).
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum LaxIpSlice<'a> {
+    /// The ipv4 header & the decoded extension headers.
+    Ipv4(LaxIpv4Slice<'a>),
+    /// The ipv6 header & the decoded extension headers.
+    Ipv6(LaxIpv6Slice<'a>),
+}
+
+impl<'a> LaxIpSlice<'a> {
+    /// Returns a reference to the `Ipv4Slice` if `self` is a `IpSlice::Ipv4`.
+    pub fn ipv4(&self) -> Option<&LaxIpv4Slice> {
+        use LaxIpSlice::*;
+        match self {
+            Ipv4(slice) => Some(slice),
+            Ipv6(_) => None,
+        }
+    }
+
+    /// Returns a reference to the `Ipv6Slice` if `self` is a `IpSlice::Ipv6`.
+    pub fn ipv6(&self) -> Option<&LaxIpv6Slice> {
+        use LaxIpSlice::*;
+        match self {
+            Ipv4(_) => None,
+            Ipv6(slice) => Some(slice),
+        }
+    }
+
+    /// Returns true if the payload is fragmented.
+    pub fn is_fragmenting_payload(&self) -> bool {
+        match self {
+            LaxIpSlice::Ipv4(s) => s.is_payload_fragmented(),
+            LaxIpSlice::Ipv6(s) => s.is_payload_fragmented(),
+        }
+    }
+
+    /// Return the source address as an std::net::Ipvddr (requires
+    /// crate feature `std`).
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn source_addr(&self) -> std::net::IpAddr {
+        match self {
+            LaxIpSlice::Ipv4(s) => s.header().source_addr().into(),
+            LaxIpSlice::Ipv6(s) => s.header().source_addr().into(),
+        }
+    }
+
+    /// Return the destination address as an std::net::IpAddr (requires
+    /// crate feature `std`).
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn destination_addr(&self) -> std::net::IpAddr {
+        match self {
+            LaxIpSlice::Ipv4(s) => s.header().destination_addr().into(),
+            LaxIpSlice::Ipv6(s) => s.header().destination_addr().into(),
+        }
+    }
+
+    /// Returns a slice containing the data after the IP header
+    /// and IP extensions headers.
+    #[inline]
+    pub fn payload(&self) -> &LaxIpPayloadSlice<'a> {
+        use LaxIpSlice::*;
+        match self {
+            Ipv4(ipv4) => ipv4.payload(),
+            Ipv6(ipv6) => ipv6.payload(),
+        }
+    }
+
+    /// Returns the ip number the type of payload of the IP packet.
+    ///
+    /// This function returns the ip number stored in the last
+    /// IP header or extension header.
+    #[inline]
+    pub fn payload_ip_number(&self) -> IpNumber {
+        use LaxIpSlice::*;
+        match self {
+            Ipv4(ipv4) => ipv4.payload().ip_number,
+            Ipv6(ipv6) => ipv6.payload().ip_number,
+        }
+    }
+
+    /// 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.
+    ///
+    /// If you want to only receive correct IpPayloads use [`IpSlice::from_ip_slice`]
+    /// instead.
+    ///
+    /// The main usecases 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.
+    /// * Parsing packets where the `total_len` (for IPv4) or `payload_length` (for IPv6)
+    ///   have not yet been set. This can be useful when parsing packets which have been
+    ///   recorded in a layer before the length field was set (e.g. before the operating
+    ///   system set the length fields).
+    ///
+    /// # Differences to `IpSlice::from_slice`:
+    ///
+    // There are two main differences:
+    ///
+    /// * Errors in the expansion headers will only stop the parsing and return an `Ok`
+    ///   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.
+    /// * `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
+    ///   used as a substitute.
+    ///
+    /// You can check if the slice length was used as a substitude 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
+    /// if the `total_length` field in the IPv4 header is:
+    ///
+    ///  * Bigger then the given slice (payload cannot fully be seperated).
+    ///  * Too small to contain at least the IPv4 header.
+    ///
+    /// For IPv6 packet the slice length is used as a fallback/substitude
+    /// if the `payload_length` is
+    ///
+    /// * Bigger then the given slice (payload cannot fully be seperated).
+    /// * The value `0`.
+    pub fn from_slice(
+        slice: &[u8],
+    ) -> Result<
+        (
+            LaxIpSlice,
+            Option<(err::ipv6_exts::HeaderSliceError, err::Layer)>,
+        ),
+        err::ip::LaxHeaderSliceError,
+    > {
+        use crate::ip_number::AUTH;
+        use err::ip::HeaderError::*;
+        use err::ip::LaxHeaderSliceError as E;
+        use err::ipv6_exts::HeaderError as SH;
+        use err::ipv6_exts::HeaderSliceError as S;
+        use LaxIpSlice::*;
+
+        if slice.is_empty() {
+            Err(E::Len(err::LenError {
+                required_len: 1,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::IpHeader,
+                layer_start_offset: 0,
+            }))
+        } else {
+            // SAFETY: Safe as slice is not empty.
+            let first_byte = unsafe { slice.get_unchecked(0) };
+            match first_byte >> 4 {
+                4 => {
+                    let ihl = first_byte & 0xf;
+
+                    // check that the ihl has at least the length of the base IPv4 header
+                    if ihl < 5 {
+                        return Err(E::Content(Ipv4HeaderLengthSmallerThanHeader { ihl }));
+                    }
+
+                    // check there is enough data for the header
+                    let header_len = (usize::from(ihl)) * 4;
+                    if slice.len() < header_len {
+                        return Err(E::Len(LenError {
+                            required_len: header_len,
+                            len: slice.len(),
+                            len_source: LenSource::Slice,
+                            layer: Layer::Ipv4Header,
+                            layer_start_offset: 0,
+                        }));
+                    }
+
+                    // SAFETY:
+                    // Safe as the slice length is checked to be at least
+                    // header_len or greater above.
+                    let header = unsafe {
+                        Ipv4HeaderSlice::from_slice_unchecked(core::slice::from_raw_parts(
+                            slice.as_ptr(),
+                            header_len,
+                        ))
+                    };
+
+                    // check the total_lenat least contains the header
+                    let total_len = usize::from(header.total_len());
+
+                    let (header_payload, len_source, incomplete) = if total_len < header_len {
+                        // fallback to slice len
+                        (
+                            unsafe {
+                                core::slice::from_raw_parts(
+                                    // SAFETY: Safe as slice.len() >= header_len was validated
+                                    // in a if statement above.
+                                    slice.as_ptr().add(header_len),
+                                    // SAFETY: Safe as slice.len() >= header_len was validated
+                                    // in a if statement above.
+                                    slice.len() - header_len,
+                                )
+                            },
+                            LenSource::Slice,
+                            false,
+                        )
+                    } else if slice.len() < total_len {
+                        // fallback to slice len
+                        (
+                            unsafe {
+                                core::slice::from_raw_parts(
+                                    // SAFETY: Safe as slice.len() >= header_len was validated
+                                    // in a if statement above.
+                                    slice.as_ptr().add(header_len),
+                                    // SAFETY: Safe as slice.len() >= header_len was validated
+                                    // in a if statement above.
+                                    slice.len() - header_len,
+                                )
+                            },
+                            LenSource::Slice,
+                            true, // flag payload as incomplete
+                        )
+                    } else {
+                        (
+                            unsafe {
+                                core::slice::from_raw_parts(
+                                    // 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
+                                    // if statement above as well as that slice.len() >= total_length_usize.
+                                    total_len - header_len,
+                                )
+                            },
+                            LenSource::Ipv4HeaderTotalLen,
+                            false,
+                        )
+                    };
+
+                    // slice extension headers
+                    // decode the authentication header if needed
+                    let fragmented = header.is_fragmenting_payload();
+                    match header.protocol() {
+                        AUTH => {
+                            use crate::err::ip_auth::HeaderSliceError as A;
+
+                            // parse extension headers
+                            match IpAuthHeaderSlice::from_slice(header_payload) {
+                                Ok(auth) => {
+                                    // remove the extension header from the payload
+                                    let payload = unsafe {
+                                        core::slice::from_raw_parts(
+                                            header_payload.as_ptr().add(auth.slice().len()),
+                                            header_payload.len() - auth.slice().len(),
+                                        )
+                                    };
+                                    Ok((
+                                        Ipv4(LaxIpv4Slice {
+                                            header,
+                                            exts: Ipv4ExtensionsSlice { auth: Some(auth) },
+                                            payload: LaxIpPayloadSlice {
+                                                incomplete,
+                                                ip_number: auth.next_header(),
+                                                fragmented,
+                                                len_source,
+                                                payload,
+                                            },
+                                        }),
+                                        None,
+                                    ))
+                                }
+                                Err(err) => {
+                                    let ip_number = header.protocol();
+                                    Ok((
+                                        Ipv4(LaxIpv4Slice {
+                                            header,
+                                            exts: Ipv4ExtensionsSlice { auth: None },
+                                            payload: LaxIpPayloadSlice {
+                                                incomplete,
+                                                ip_number,
+                                                fragmented,
+                                                len_source,
+                                                payload: header_payload,
+                                            },
+                                        }),
+                                        match err {
+                                            A::Len(mut l) => Some((
+                                                S::Len({
+                                                    l.len_source = len_source;
+                                                    l.add_offset(header.slice().len())
+                                                }),
+                                                err::Layer::IpAuthHeader,
+                                            )),
+                                            A::Content(l) => Some((
+                                                S::Content(SH::IpAuth(l)),
+                                                err::Layer::IpAuthHeader,
+                                            )),
+                                        },
+                                    ))
+                                }
+                            }
+                        }
+                        ip_number => Ok((
+                            Ipv4(LaxIpv4Slice {
+                                header,
+                                exts: Ipv4ExtensionsSlice { auth: None },
+                                payload: LaxIpPayloadSlice {
+                                    incomplete,
+                                    ip_number,
+                                    fragmented,
+                                    len_source,
+                                    payload: header_payload,
+                                },
+                            }),
+                            None,
+                        )),
+                    }
+                }
+                6 => {
+                    // check length
+                    if slice.len() < Ipv6Header::LEN {
+                        return Err(E::Len(LenError {
+                            required_len: Ipv6Header::LEN,
+                            len: slice.len(),
+                            len_source: LenSource::Slice,
+                            layer: Layer::Ipv6Header,
+                            layer_start_offset: 0,
+                        }));
+                    }
+
+                    let header = unsafe {
+                        Ipv6HeaderSlice::from_slice_unchecked(core::slice::from_raw_parts(
+                            slice.as_ptr(),
+                            Ipv6Header::LEN,
+                        ))
+                    };
+
+                    // restrict slice by the length specified in the header (if possible)
+                    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
+                            // length and use the slice length as a fallback value
+                            // TODO: Add payload length parsing from the jumbogram for the zero case
+                            (
+                                unsafe {
+                                    core::slice::from_raw_parts(
+                                        slice.as_ptr().add(Ipv6Header::LEN),
+                                        slice.len() - Ipv6Header::LEN,
+                                    )
+                                },
+                                LenSource::Slice,
+                                false,
+                            )
+                        } else if slice.len() - Ipv6Header::LEN < payload_len {
+                            // slice is smaller then the assumed payload length
+                            (
+                                unsafe {
+                                    core::slice::from_raw_parts(
+                                        slice.as_ptr().add(Ipv6Header::LEN),
+                                        slice.len() - Ipv6Header::LEN,
+                                    )
+                                },
+                                LenSource::Slice,
+                                true, // incomplete
+                            )
+                        } else {
+                            // all good, all data should be here
+                            (
+                                unsafe {
+                                    core::slice::from_raw_parts(
+                                        slice.as_ptr().add(Ipv6Header::LEN),
+                                        payload_len,
+                                    )
+                                },
+                                LenSource::Ipv6HeaderPayloadLen,
+                                false,
+                            )
+                        };
+
+                    // parse extension headers
+                    let (exts, payload_ip_number, payload, mut ext_stop_err) =
+                        Ipv6ExtensionsSlice::from_slice_lax(header.next_header(), header_payload);
+
+                    // add len offset
+                    if let Some((S::Len(l), _)) = ext_stop_err.as_mut() {
+                        l.len_source = len_source;
+                        l.layer_start_offset += header.header_len();
+                    }
+                    let fragmented = exts.is_fragmenting_payload();
+                    Ok((
+                        Ipv6(LaxIpv6Slice {
+                            header,
+                            exts,
+                            payload: LaxIpPayloadSlice {
+                                incomplete,
+                                ip_number: payload_ip_number,
+                                fragmented,
+                                len_source,
+                                payload,
+                            },
+                        }),
+                        ext_stop_err,
+                    ))
+                }
+                version_number => Err(E::Content(UnsupportedIpVersion { version_number })),
+            }
+        }
+    }
+}
+
+impl<'a> From<LaxIpv4Slice<'a>> for LaxIpSlice<'a> {
+    fn from(value: LaxIpv4Slice<'a>) -> Self {
+        LaxIpSlice::Ipv4(value)
+    }
+}
+
+impl<'a> From<LaxIpv6Slice<'a>> for LaxIpSlice<'a> {
+    fn from(value: LaxIpv6Slice<'a>) -> Self {
+        LaxIpSlice::Ipv6(value)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+
+    #[test]
+    fn debug_clone_eq() {
+        // ipv4
+        {
+            let mut header: Ipv4Header = Default::default();
+            header.protocol = ip_number::UDP;
+            header.set_payload_len(0).unwrap();
+            let buffer = header.to_bytes();
+
+            let ipv4 = LaxIpv4Slice::from_slice(&buffer).unwrap().0;
+            let slice = LaxIpSlice::Ipv4(ipv4.clone());
+
+            // clone & eq
+            assert_eq!(slice.clone(), slice);
+
+            // debug
+            assert_eq!(format!("{:?}", slice), format!("Ipv4({:?})", ipv4));
+        }
+        // ipv6
+        {
+            let header = Ipv6Header {
+                payload_length: 0,
+                next_header: ip_number::UDP,
+                ..Default::default()
+            };
+            let buffer = header.to_bytes();
+            let ipv6 = LaxIpv6Slice::from_slice(&buffer).unwrap().0;
+            let slice = LaxIpSlice::Ipv6(ipv6.clone());
+
+            // clone & eq
+            assert_eq!(slice.clone(), slice);
+
+            // debug
+            assert_eq!(format!("{:?}", slice), format!("Ipv6({:?})", ipv6));
+        }
+    }
+
+    #[test]
+    fn is_fragmenting_payload() {
+        for fragment in [false, true] {
+            use ip_number::UDP;
+            // ipv4
+            {
+                let mut ipv4 = Ipv4Header::new(0, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10]).unwrap();
+                if fragment {
+                    ipv4.fragment_offset = 123.try_into().unwrap();
+                }
+
+                let data = ipv4.to_bytes();
+                let ipv4_slice = LaxIpv4Slice::from_slice(&data).unwrap().0;
+                assert_eq!(
+                    fragment,
+                    LaxIpSlice::Ipv4(ipv4_slice).is_fragmenting_payload()
+                );
+            }
+
+            // ipv6
+            {
+                let ipv6_frag = Ipv6FragmentHeader {
+                    next_header: UDP,
+                    fragment_offset: IpFragOffset::ZERO,
+                    more_fragments: fragment,
+                    identification: 0,
+                };
+                let ipv6 = Ipv6Header {
+                    traffic_class: 0,
+                    flow_label: 1.try_into().unwrap(),
+                    payload_length: ipv6_frag.header_len() as u16,
+                    next_header: ip_number::IPV6_FRAG,
+                    hop_limit: 4,
+                    source: [1; 16],
+                    destination: [2; 16],
+                };
+                let mut data = Vec::with_capacity(ipv6.header_len() + ipv6_frag.header_len());
+                data.extend_from_slice(&ipv6.to_bytes());
+                data.extend_from_slice(&ipv6_frag.to_bytes());
+
+                assert_eq!(
+                    fragment,
+                    LaxIpSlice::Ipv6(LaxIpv6Slice::from_slice(&data).unwrap().0)
+                        .is_fragmenting_payload()
+                );
+            }
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn source_addr() {
+        // ipv4
+        {
+            let data = Ipv4Header::new(0, 1, 2.into(), [3, 4, 5, 6], [7, 8, 9, 10])
+                .unwrap()
+                .to_bytes();
+            assert_eq!(
+                IpAddr::V4(Ipv4Addr::from([3, 4, 5, 6])),
+                LaxIpSlice::Ipv4(LaxIpv4Slice::from_slice(&data[..]).unwrap().0).source_addr()
+            );
+        }
+
+        // ipv6
+        {
+            let data = Ipv6Header {
+                traffic_class: 0,
+                flow_label: 1.try_into().unwrap(),
+                payload_length: 0,
+                next_header: ip_number::IGMP,
+                hop_limit: 4,
+                source: [1; 16],
+                destination: [2; 16],
+            }
+            .to_bytes();
+
+            assert_eq!(
+                IpAddr::V6(Ipv6Addr::from([1; 16])),
+                LaxIpSlice::Ipv6(LaxIpv6Slice::from_slice(&data[..]).unwrap().0).source_addr()
+            );
+        }
+    }
+
+    #[cfg(feature = "std")]
+    #[test]
+    fn destination_addr() {
+        use crate::ip_number::UDP;
+
+        // ipv4
+        {
+            let data = Ipv4Header::new(0, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10])
+                .unwrap()
+                .to_bytes();
+
+            assert_eq!(
+                IpAddr::V4(Ipv4Addr::from([7, 8, 9, 10])),
+                LaxIpSlice::Ipv4(LaxIpv4Slice::from_slice(&data[..]).unwrap().0).destination_addr()
+            );
+        }
+
+        // ipv6
+        {
+            let data = Ipv6Header {
+                traffic_class: 0,
+                flow_label: 1.try_into().unwrap(),
+                payload_length: 0,
+                next_header: ip_number::IGMP,
+                hop_limit: 4,
+                source: [1; 16],
+                destination: [2; 16],
+            }
+            .to_bytes();
+
+            assert_eq!(
+                IpAddr::V6(Ipv6Addr::from([2; 16])),
+                LaxIpSlice::Ipv6(LaxIpv6Slice::from_slice(&data).unwrap().0).destination_addr()
+            );
+        }
+    }
+
+    #[test]
+    fn ip_payload() {
+        let payload: [u8; 4] = [1, 2, 3, 4];
+        // ipv4
+        {
+            let header = Ipv4Header::new(
+                payload.len() as u16,
+                1,
+                ip_number::UDP,
+                [3, 4, 5, 6],
+                [7, 8, 9, 10],
+            )
+            .unwrap();
+            let mut data = Vec::with_capacity(header.header_len() + payload.len());
+            data.extend_from_slice(&header.to_bytes());
+            data.extend_from_slice(&payload);
+            assert_eq!(
+                LaxIpSlice::Ipv4(LaxIpv4Slice::from_slice(&data[..]).unwrap().0).payload(),
+                &LaxIpPayloadSlice {
+                    incomplete: false,
+                    ip_number: ip_number::UDP.into(),
+                    fragmented: header.is_fragmenting_payload(),
+                    len_source: LenSource::Ipv4HeaderTotalLen,
+                    payload: &payload,
+                }
+            );
+        }
+
+        // ipv6
+        {
+            let header = Ipv6Header {
+                traffic_class: 0,
+                flow_label: 1.try_into().unwrap(),
+                payload_length: payload.len() as u16,
+                next_header: ip_number::UDP,
+                hop_limit: 4,
+                source: [1; 16],
+                destination: [2; 16],
+            };
+            let mut data = Vec::with_capacity(header.header_len() + payload.len());
+            data.extend_from_slice(&header.to_bytes());
+            data.extend_from_slice(&payload);
+            assert_eq!(
+                LaxIpSlice::Ipv6(LaxIpv6Slice::from_slice(&data[..]).unwrap().0).payload(),
+                &LaxIpPayloadSlice {
+                    incomplete: false,
+                    ip_number: ip_number::UDP.into(),
+                    fragmented: false,
+                    len_source: LenSource::Ipv6HeaderPayloadLen,
+                    payload: &payload,
+                }
+            );
+        }
+    }
+
+    #[test]
+    fn payload_ip_number() {
+        use crate::ip_number::{IGMP, UDP};
+
+        // ipv4
+        {
+            let data = Ipv4Header::new(0, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10])
+                .unwrap()
+                .to_bytes();
+            assert_eq!(
+                UDP,
+                LaxIpSlice::Ipv4(LaxIpv4Slice::from_slice(&data[..]).unwrap().0)
+                    .payload_ip_number()
+            );
+        }
+
+        // ipv6
+        {
+            let data = Ipv6Header {
+                traffic_class: 0,
+                flow_label: 1.try_into().unwrap(),
+                payload_length: 0,
+                next_header: IGMP,
+                hop_limit: 4,
+                source: [1; 16],
+                destination: [2; 16],
+            }
+            .to_bytes();
+
+            assert_eq!(
+                IGMP,
+                LaxIpSlice::Ipv6(LaxIpv6Slice::from_slice(&data).unwrap().0).payload_ip_number()
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_ip_slice(
+            ipv4_header in ipv4_any(),
+            ipv4_exts in ipv4_extensions_with(ip_number::UDP),
+            ipv6_header in ipv6_any(),
+            mut ipv6_exts in ipv6_extensions_with(ip_number::UDP)
+        ) {
+            use err::ip::HeaderError::*;
+            use err::ip::LaxHeaderSliceError as E;
+            use err::ipv6_exts::HeaderSliceError as S;
+            use err::ip_auth::HeaderError::*;
+            use crate::IpHeaders;
+
+            // zero payload
+            assert_eq!(
+                LaxIpSlice::from_slice(&[]),
+                Err(E::Len(LenError{
+                    required_len: 1,
+                    len: 0,
+                    len_source: LenSource::Slice,
+                    layer: Layer::IpHeader,
+                    layer_start_offset: 0,
+                }))
+            );
+
+            // unknown version number
+            for bad_version in 0..0xfu8 {
+                if bad_version != 4 && bad_version != 6 {
+                    assert_eq!(
+                        LaxIpSlice::from_slice(&[bad_version << 4]),
+                        Err(E::Content(UnsupportedIpVersion {
+                            version_number: bad_version,
+                        }))
+                    );
+                }
+            }
+
+            let payload = [1,2,3,4];
+
+            // IPv4
+            {
+                // setup header length & fields
+                let ipv4_header = {
+                    let mut header = ipv4_header;
+                    header.protocol = if ipv4_exts.auth.is_some() {
+                        ip_number::AUTH
+                    } else {
+                        ip_number::UDP
+                    };
+                    header.total_len = (header.header_len() + ipv4_exts.header_len() + payload.len()) as u16;
+                    header.header_checksum = header.calc_header_checksum();
+                    header
+                };
+
+                let ipv4 = IpHeaders::Ipv4(
+                    ipv4_header.clone(),
+                    ipv4_exts.clone()
+                );
+
+                // build packet
+                let mut buffer = Vec::with_capacity(ipv4.header_len() + payload.len());
+                ipv4.write(&mut buffer).unwrap();
+                buffer.extend_from_slice(&payload);
+
+                // happy path v4
+                {
+                    // run test
+                    let (actual, actual_stop_err) = LaxIpSlice::from_slice(&buffer).unwrap();
+                    assert_eq!(None, actual_stop_err);
+                    assert!(actual.ipv6().is_none());
+                    let actual = actual.ipv4().unwrap().clone();
+                    assert_eq!(actual.header.to_header(), ipv4_header);
+                    assert_eq!(actual.extensions().to_header(), ipv4_exts);
+                    assert_eq!(
+                        actual.payload,
+                        LaxIpPayloadSlice{
+                            incomplete: false,
+                            ip_number: ip_number::UDP.into(),
+                            fragmented: ipv4_header.is_fragmenting_payload(),
+                            len_source: LenSource::Ipv4HeaderTotalLen,
+                            payload: &payload
+                        }
+                    );
+                }
+
+                // ihl smaller then 5 error
+                for bad_ihl in 0..5u8 {
+                    let mut buffer = buffer.clone();
+
+                    // inject bad IHL
+                    buffer[0] = (buffer[0] & 0xf0u8) | bad_ihl;
+
+                    assert_eq!(
+                        LaxIpSlice::from_slice(&buffer),
+                        Err(E::Content(Ipv4HeaderLengthSmallerThanHeader { ihl: bad_ihl }))
+                    );
+                }
+
+                // slice smaller then header error
+                for bad_len in 1..ipv4_header.header_len() {
+                    assert_eq!(
+                        LaxIpSlice::from_slice(&buffer[..bad_len]),
+                        Err(E::Len(LenError{
+                            required_len: ipv4_header.header_len(),
+                            len: bad_len,
+                            len_source: LenSource::Slice,
+                            layer: Layer::Ipv4Header,
+                            layer_start_offset: 0,
+                        }))
+                    );
+                }
+
+                // total len smaller then header
+                for bad_len in 1..ipv4_header.header_len() {
+                    let mut buffer = buffer.clone();
+
+                    // inject bad total length
+                    let bad_len_be = (bad_len as u16).to_be_bytes();
+                    buffer[2] = bad_len_be[0];
+                    buffer[3] = bad_len_be[1];
+
+                    // expect a valid parse with length source "slice"
+                    let (actual, actual_stop_err) = LaxIpSlice::from_slice(&buffer).unwrap();
+                    assert_eq!(None, actual_stop_err);
+                    let actual = actual.ipv4().unwrap().clone();
+                    let mut expected_header = ipv4_header.clone();
+                    expected_header.total_len = bad_len as u16;
+                    assert_eq!(actual.header.to_header(), expected_header);
+                    assert_eq!(actual.extensions().to_header(), ipv4_exts);
+                    assert_eq!(
+                        actual.payload,
+                        LaxIpPayloadSlice{
+                            incomplete: false,
+                            ip_number: ip_number::UDP.into(),
+                            fragmented: ipv4_header.is_fragmenting_payload(),
+                            len_source: LenSource::Slice,
+                            payload: &payload
+                        }
+                    );
+                }
+
+                // total len bigger then slice
+                {
+                    let bad_len = (buffer.len() + 1) as u16;
+                    let mut buffer = buffer.clone();
+
+                    // inject bad total length
+                    let bad_len_be = (bad_len as u16).to_be_bytes();
+                    buffer[2] = bad_len_be[0];
+                    buffer[3] = bad_len_be[1];
+
+                    // expect a valid parse with length source "slice" & incomplete set
+                    let (actual, actual_stop_err) = LaxIpSlice::from_slice(&buffer).unwrap();
+                    assert_eq!(None, actual_stop_err);
+                    let actual = actual.ipv4().unwrap().clone();
+                    let mut expected_header = ipv4_header.clone();
+                    expected_header.total_len = bad_len as u16;
+                    assert_eq!(actual.header.to_header(), expected_header);
+                    assert_eq!(actual.extensions().to_header(), ipv4_exts);
+                    assert_eq!(
+                        actual.payload,
+                        LaxIpPayloadSlice{
+                            incomplete: true,
+                            ip_number: ip_number::UDP.into(),
+                            fragmented: ipv4_header.is_fragmenting_payload(),
+                            len_source: LenSource::Slice,
+                            payload: &payload
+                        }
+                    );
+                }
+
+                // auth ext header len error
+                if ipv4_exts.auth.is_some() {
+                    let bad_len = ipv4_header.header_len() + ipv4_exts.header_len() - 1;
+
+                    let (actual, actual_stop_err) = LaxIpSlice::from_slice(&buffer[..bad_len]).unwrap();
+                    assert_eq!(
+                        actual_stop_err,
+                        Some((
+                            S::Len(LenError{
+                                required_len: ipv4_exts.header_len(),
+                                len: bad_len - ipv4_header.header_len(),
+                                len_source: LenSource::Slice,
+                                layer: Layer::IpAuthHeader,
+                                layer_start_offset: ipv4_header.header_len(),
+                            }),
+                            Layer::IpAuthHeader
+                        ))
+                    );
+                    assert_eq!(actual.ipv4().unwrap().clone().header().to_header(), ipv4_header);
+                    assert_eq!(
+                        actual.payload(),
+                        &LaxIpPayloadSlice{
+                            incomplete: true,
+                            ip_number: ip_number::AUTH,
+                            fragmented: ipv4_header.is_fragmenting_payload(),
+                            len_source: LenSource::Slice,
+                            payload: &buffer[ipv4_header.header_len()..bad_len],
+                        }
+                    );
+                }
+
+                // auth ext header content error
+                if ipv4_exts.auth.is_some() {
+                    let mut buffer = buffer.clone();
+                    buffer[ipv4_header.header_len() + 1] = 0;
+
+                    let (actual, actual_stop_err) = LaxIpSlice::from_slice(&buffer).unwrap();
+
+                    assert_eq!(
+                        actual_stop_err,
+                        Some((
+                            S::Content(ipv6_exts::HeaderError::IpAuth(ZeroPayloadLen)),
+                            Layer::IpAuthHeader
+                        ))
+                    );
+                    assert_eq!(actual.ipv4().unwrap().clone().header().to_header(), ipv4_header);
+                    assert_eq!(
+                        actual.payload(),
+                        &LaxIpPayloadSlice{
+                            incomplete: false,
+                            ip_number: ip_number::AUTH,
+                            fragmented: ipv4_header.is_fragmenting_payload(),
+                            len_source: LenSource::Ipv4HeaderTotalLen,
+                            payload: &buffer[ipv4_header.header_len()..],
+                        }
+                    );
+                }
+            }
+
+            // IPv6
+            {
+                let ipv6_header = {
+                    let mut header = ipv6_header;
+                    header.next_header = ipv6_exts.set_next_headers(ip_number::UDP);
+                    header.payload_length = (ipv6_exts.header_len() + payload.len()) as u16;
+                    header
+                };
+
+                let ipv6 = IpHeaders::Ipv6(
+                    ipv6_header.clone(),
+                    ipv6_exts.clone()
+                );
+
+                // build packet
+                let mut buffer = Vec::with_capacity(ipv6.header_len() + payload.len());
+                ipv6.write(&mut buffer).unwrap();
+                buffer.extend_from_slice(&payload);
+
+                // happy path v6
+                {
+                    // run test
+                    let (actual, actual_stop_err) = LaxIpSlice::from_slice(&buffer).unwrap();
+                    assert_eq!(None, actual_stop_err);
+                    assert!(actual.ipv4().is_none());
+                    let actual = actual.ipv6().unwrap().clone();
+                    assert_eq!(actual.header.to_header(), ipv6_header);
+                    assert_eq!(
+                        Ipv6Extensions::from_slice(
+                            ipv6_header.next_header,
+                            actual.extensions().slice()
+                        ).unwrap().0,
+                        ipv6_exts
+                    );
+                    assert_eq!(
+                        actual.payload,
+                        LaxIpPayloadSlice{
+                            incomplete: false,
+                            ip_number: ip_number::UDP.into(),
+                            fragmented: ipv6_exts.is_fragmenting_payload(),
+                            len_source: LenSource::Ipv6HeaderPayloadLen,
+                            payload: &payload
+                        }
+                    );
+                }
+
+                // len error when parsing header
+                for bad_len in 1..ipv6_header.header_len() {
+                    assert_eq!(
+                        LaxIpSlice::from_slice(&buffer[..bad_len]),
+                        Err(E::Len(LenError{
+                            required_len: ipv6_header.header_len(),
+                            len: bad_len,
+                            len_source: LenSource::Slice,
+                            layer: Layer::Ipv6Header,
+                            layer_start_offset: 0,
+                        }))
+                    );
+                }
+
+                // ipv6 with zero payload length (should fallback to the slice length)
+                {
+                    let mut buffer = buffer.clone();
+
+                    // inject 0 as payload len
+                    buffer[4] = 0;
+                    buffer[5] = 0;
+
+                    // run test
+                    let (actual, actual_stop_err) = LaxIpSlice::from_slice(&buffer).unwrap();
+                    assert_eq!(None, actual_stop_err);
+                    let actual = actual.ipv6().unwrap().clone();
+                    let mut expected_header = ipv6_header.clone();
+                    expected_header.payload_length = 0;
+                    assert_eq!(actual.header.to_header(), expected_header);
+                    assert_eq!(
+                        Ipv6Extensions::from_slice(
+                            ipv6_header.next_header,
+                            actual.extensions().slice()
+                        ).unwrap().0,
+                        ipv6_exts
+                    );
+                    assert_eq!(
+                        actual.payload,
+                        LaxIpPayloadSlice{
+                            incomplete: false,
+                            ip_number: ip_number::UDP.into(),
+                            fragmented: ipv6_exts.is_fragmenting_payload(),
+                            len_source: LenSource::Slice,
+                            payload: &payload
+                        }
+                    );
+                }
+
+                // payload len bigger then slice
+                {
+                    let mut buffer = buffer.clone();
+
+                    // inject 0 as payload len
+                    let bad_payload_len = (buffer.len() - ipv6_header.header_len() + 1) as u16;
+                    let bad_payload_len_be = bad_payload_len.to_be_bytes();
+                    buffer[4] = bad_payload_len_be[0];
+                    buffer[5] = bad_payload_len_be[1];
+
+                    // run test
+                    let (actual, actual_stop_err) = LaxIpSlice::from_slice(&buffer).unwrap();
+                    assert_eq!(None, actual_stop_err);
+                    let actual = actual.ipv6().unwrap().clone();
+                    let mut expected_header = ipv6_header.clone();
+                    expected_header.payload_length = bad_payload_len;
+
+                    assert_eq!(actual.header.to_header(), expected_header);
+                    assert_eq!(
+                        Ipv6Extensions::from_slice(
+                            ipv6_header.next_header,
+                            actual.extensions().slice()
+                        ).unwrap().0,
+                        ipv6_exts
+                    );
+                    assert_eq!(
+                        actual.payload,
+                        LaxIpPayloadSlice{
+                            incomplete: true,
+                            ip_number: ip_number::UDP.into(),
+                            fragmented: ipv6_exts.is_fragmenting_payload(),
+                            len_source: LenSource::Slice,
+                            payload: &payload
+                        }
+                    );
+                }
+
+                // extension length error
+                if ipv6_exts.hop_by_hop_options.is_some() {
+                    let bad_len = Ipv6Header::LEN + 1;
+
+                    let (actual, actual_stop_err) = LaxIpSlice::from_slice(&buffer[..bad_len]).unwrap();
+
+                    let actual = actual.ipv6().unwrap().clone();
+                    assert_eq!(actual.header.to_header(), ipv6_header);
+                    assert_eq!(
+                        actual_stop_err,
+                        Some((
+                            S::Len(LenError{
+                                required_len: 8,
+                                len: bad_len - ipv6_header.header_len(),
+                                len_source: LenSource::Slice,
+                                layer: Layer::Ipv6ExtHeader,
+                                layer_start_offset: ipv6_header.header_len(),
+                            }),
+                            Layer::Ipv6HopByHopHeader
+                        ))
+                    );
+                    assert_eq!(
+                        actual.payload,
+                        LaxIpPayloadSlice{
+                            incomplete: true,
+                            ip_number: ip_number::IPV6_HOP_BY_HOP,
+                            fragmented: false, // fragment header will not be able to be read
+                            len_source: LenSource::Slice,
+                            payload: &buffer[ipv6_header.header_len()..bad_len]
+                        }
+                    );
+                }
+
+                // extension content error
+                if ipv6_exts.auth.is_some() {
+
+                    // introduce a auth header zero payload error
+                    let mut buffer = buffer.clone();
+                    let auth_offset = ipv6_header.header_len() +
+                        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
+                        ipv6_exts.fragment.as_ref().map(|h| h.header_len()).unwrap_or(0);
+
+                    // inject length zero into auth header (not valid, will
+                    // trigger a content error)
+                    buffer[auth_offset + 1] = 0;
+
+                    let (actual, actual_stop_err) = LaxIpSlice::from_slice(&buffer).unwrap();
+                    let actual = actual.ipv6().unwrap().clone();
+                    assert_eq!(actual.header.to_header(), ipv6_header);
+                    assert_eq!(
+                        actual_stop_err,
+                        Some((
+                            S::Content(ipv6_exts::HeaderError::IpAuth(ZeroPayloadLen)),
+                            Layer::IpAuthHeader,
+                        ))
+                    );
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_ipv4_slice(
+            ipv4_header in ipv4_unknown()
+        ) {
+            let mut header = ipv4_header.clone();
+            header.total_len = (header.header_len() + 4) as u16;
+
+            let mut buffer = Vec::with_capacity(header.total_len.into());
+            buffer.extend_from_slice(&header.to_bytes()[..]);
+            buffer.extend_from_slice(&[1,2,3,4]);
+            let s = LaxIpv4Slice::from_slice(&buffer).unwrap().0;
+            let actual: LaxIpSlice = s.clone().into();
+            assert_eq!(LaxIpSlice::Ipv4(s), actual);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_ipv6_slice(
+            ipv6_header in ipv6_unknown()
+        ) {
+            let mut header = ipv6_header.clone();
+            header.payload_length = 4;
+
+            let mut buffer = Vec::with_capacity(header.header_len() + 4);
+            buffer.extend_from_slice(&header.to_bytes()[..]);
+            buffer.extend_from_slice(&[1,2,3,4]);
+            let s = LaxIpv6Slice::from_slice(&buffer).unwrap().0;
+            let actual: LaxIpSlice = s.clone().into();
+            assert_eq!(LaxIpSlice::Ipv6(s), actual);
+        }
+    }
+}
diff --git a/src/net/lax_ipv4_slice.rs b/src/net/lax_ipv4_slice.rs
new file mode 100644
index 0000000..2c73f58
--- /dev/null
+++ b/src/net/lax_ipv4_slice.rs
@@ -0,0 +1,527 @@
+use crate::*;
+
+/// Slice containing laxly separated IPv4 headers & payload.
+///
+/// Compared to the normal [`Ipv4Slice`] this slice allows the
+/// payload to be incomplete/cut off and errors to be present in
+/// the IpPayload.
+///
+/// The main usecases 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.
+/// * Parsing packets where the `total_len` (for IPv4) have not yet been set.
+///   This can be useful when parsing packets which have been recorded in a
+///   layer before the length field was set (e.g. before the operating
+///   system set the length fields).
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct LaxIpv4Slice<'a> {
+    pub(crate) header: Ipv4HeaderSlice<'a>,
+    pub(crate) exts: Ipv4ExtensionsSlice<'a>,
+    pub(crate) payload: LaxIpPayloadSlice<'a>,
+}
+
+impl<'a> LaxIpv4Slice<'a> {
+    /// Separates and validates IPv4 headers (including extension headers) &
+    /// 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 [`Ipv4Slice::from_slice`]
+    /// instead.
+    ///
+    /// The main usecases 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.
+    /// * Parsing packets where the `total_len` (for IPv4) have not yet been set.
+    ///   This can be useful when parsing packets which have been recorded in a
+    ///   layer before the length field was set (e.g. before the operating
+    ///   system set the length fields).
+    ///
+    /// # Differences to `Ipv4Slice::from_slice`:
+    ///
+    /// There are two main differences:
+    ///
+    /// * Errors in the expansion headers will only stop the parsing and return an `Ok`
+    ///   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.
+    /// * `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
+    ///   used as a substitute.
+    ///
+    /// ## What happens in the `total_len` value is inconsistent?
+    ///
+    /// When the total_length value in the IPv4 header is inconsistent the
+    /// 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).
+    ///  * 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
+    /// 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`].
+
+    pub fn from_slice(
+        slice: &[u8],
+    ) -> Result<(LaxIpv4Slice, Option<err::ip_auth::HeaderSliceError>), err::ipv4::HeaderSliceError>
+    {
+        use crate::ip_number::AUTH;
+
+        // decode the header
+        let header = Ipv4HeaderSlice::from_slice(slice)?;
+
+        // validate total_len at least contains the header
+        let header_total_len: usize = header.total_len().into();
+        let (header_payload, len_source, incomplete) = if header_total_len < header.slice().len() {
+            // total_length is smaller then the header itself
+            // fall back to the slice for the length
+            (
+                unsafe {
+                    core::slice::from_raw_parts(
+                        slice.as_ptr().add(header.slice().len()),
+                        slice.len() - header.slice().len(),
+                    )
+                },
+                LenSource::Slice,
+                // note that we have no indication that the packet is incomplete
+                false,
+            )
+        } else if header_total_len > slice.len() {
+            // more data was expected, fallback to slice and report payload as "incomplete"
+            (
+                unsafe {
+                    core::slice::from_raw_parts(
+                        slice.as_ptr().add(header.slice().len()),
+                        slice.len() - header.slice().len(),
+                    )
+                },
+                LenSource::Slice,
+                true, // incomplete
+            )
+        } else {
+            // all good the packet seems to be complete
+            (
+                unsafe {
+                    core::slice::from_raw_parts(
+                        slice.as_ptr().add(header.slice().len()),
+                        header_total_len - header.slice().len(),
+                    )
+                },
+                LenSource::Ipv4HeaderTotalLen,
+                false,
+            )
+        };
+
+        // decode the authentication header if needed
+        let fragmented = header.is_fragmenting_payload();
+        match header.protocol() {
+            AUTH => {
+                use crate::err::ip_auth::HeaderSliceError as E;
+
+                // parse extension headers
+                match IpAuthHeaderSlice::from_slice(header_payload) {
+                    Ok(auth) => {
+                        // remove the extension header from the payload
+                        let payload = unsafe {
+                            core::slice::from_raw_parts(
+                                header_payload.as_ptr().add(auth.slice().len()),
+                                header_payload.len() - auth.slice().len(),
+                            )
+                        };
+                        let ip_number = auth.next_header();
+                        Ok((
+                            LaxIpv4Slice {
+                                header,
+                                exts: Ipv4ExtensionsSlice { auth: Some(auth) },
+                                payload: LaxIpPayloadSlice {
+                                    incomplete,
+                                    ip_number,
+                                    fragmented,
+                                    len_source,
+                                    payload,
+                                },
+                            },
+                            None,
+                        ))
+                    }
+                    Err(err) => {
+                        let err = match err {
+                            E::Len(mut l) => {
+                                // change the length source to the ipv4 header
+                                l.len_source = len_source;
+                                l.layer_start_offset += header.slice().len();
+                                E::Len(l)
+                            }
+                            E::Content(err) => E::Content(err),
+                        };
+                        Ok((
+                            LaxIpv4Slice {
+                                header,
+                                exts: Ipv4ExtensionsSlice { auth: None },
+                                payload: LaxIpPayloadSlice {
+                                    incomplete,
+                                    ip_number: AUTH,
+                                    fragmented,
+                                    len_source,
+                                    payload: header_payload,
+                                },
+                            },
+                            Some(err),
+                        ))
+                    }
+                }
+            }
+            ip_number => Ok((
+                LaxIpv4Slice {
+                    header,
+                    exts: Ipv4ExtensionsSlice { auth: None },
+                    payload: LaxIpPayloadSlice {
+                        incomplete,
+                        ip_number,
+                        fragmented,
+                        len_source,
+                        payload: header_payload,
+                    },
+                },
+                None,
+            )),
+        }
+    }
+
+    /// Returns a slice containing the IPv4 header.
+    #[inline]
+    pub fn header(&self) -> Ipv4HeaderSlice {
+        self.header
+    }
+
+    /// Returns a slice containing the IPv4 extension headers.
+    #[inline]
+    pub fn extensions(&self) -> Ipv4ExtensionsSlice {
+        self.exts
+    }
+
+    /// Returns a slice containing the data after the IPv4 header
+    /// and IPv4 extensions headers.
+    #[inline]
+    pub fn payload(&self) -> &LaxIpPayloadSlice<'a> {
+        &self.payload
+    }
+
+    /// Returns the ip number the type of payload of the IPv4 packet.
+    ///
+    /// This function returns the ip number stored in the last
+    /// IPv4 header or extension header.
+    #[inline]
+    pub fn payload_ip_number(&self) -> IpNumber {
+        self.payload.ip_number
+    }
+
+    /// Returns true if the payload is flagged as being fragmented.
+    #[inline]
+    pub fn is_payload_fragmented(&self) -> bool {
+        self.header.is_fragmenting_payload()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::{err::LenError, ip_number::AUTH, test_gens::*};
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn debug_clone_eq(
+            ipv4_base in ipv4_any(),
+            auth in ip_auth_any()
+        ) {
+            let payload: [u8;4] = [1,2,3,4];
+            let mut data = Vec::with_capacity(
+                ipv4_base.header_len() +
+                auth.header_len() +
+                payload.len()
+            );
+            let mut ipv4 = ipv4_base.clone();
+            ipv4.protocol = crate::ip_number::AUTH;
+            ipv4.set_payload_len(auth.header_len() + payload.len()).unwrap();
+            data.extend_from_slice(&ipv4.to_bytes());
+            data.extend_from_slice(&auth.to_bytes());
+            data.extend_from_slice(&payload);
+
+            // decode packet
+            let (slice, _) = LaxIpv4Slice::from_slice(&data).unwrap();
+
+            // check debug output
+            prop_assert_eq!(
+                format!("{:?}", slice),
+                format!(
+                    "LaxIpv4Slice {{ header: {:?}, exts: {:?}, payload: {:?} }}",
+                    slice.header(),
+                    slice.extensions(),
+                    slice.payload()
+                )
+            );
+            prop_assert_eq!(slice.clone(), slice);
+        }
+    }
+
+    fn combine_v4(
+        v4: &Ipv4Header,
+        ext: &crate::Ipv4Extensions,
+        payload: &[u8],
+    ) -> crate::IpHeaders {
+        use crate::ip_number::UDP;
+        crate::IpHeaders::Ipv4(
+            {
+                let mut v4 = v4.clone();
+                v4.protocol = if ext.auth.is_some() { AUTH } else { UDP };
+                v4.total_len = (v4.header_len() + ext.header_len() + payload.len()) as u16;
+                v4.header_checksum = v4.calc_header_checksum();
+                v4
+            },
+            ext.clone(),
+        )
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            v4 in ipv4_any(),
+            v4_exts in ipv4_extensions_any()
+        ) {
+            use crate::err::{self, ipv4::HeaderError::*};
+            use crate::err::ipv4::HeaderSliceError as E;
+            use err::ip_auth::HeaderSliceError as A;
+
+            let payload = [1,2,3,4];
+
+            // empty error
+            assert_eq!(
+                LaxIpv4Slice::from_slice(&[]),
+                Err(E::Len(err::LenError {
+                    required_len: 20,
+                    len: 0,
+                    len_source: LenSource::Slice,
+                    layer: err::Layer::Ipv4Header,
+                    layer_start_offset: 0,
+                }))
+            );
+
+            // build a buffer with a valid packet
+            let header = combine_v4(&v4, &v4_exts, &payload);
+            let mut buffer = Vec::with_capacity(header.header_len() + payload.len() + 1);
+            header.write(&mut buffer).unwrap();
+            buffer.extend_from_slice(&payload);
+            buffer.push(1); // add some value to check the return slice
+
+            // normal read
+            {
+                let (actual, actual_stop_err) = LaxIpv4Slice::from_slice(&buffer).unwrap();
+                assert_eq!(None, actual_stop_err);
+                assert_eq!(&actual.header.to_header(), header.ipv4().unwrap().0);
+                assert_eq!(&actual.extensions().to_header(), header.ipv4().unwrap().1);
+                assert_eq!(
+                    actual.payload,
+                    LaxIpPayloadSlice{
+                        incomplete: false,
+                        ip_number: header.next_header().unwrap(),
+                        fragmented: header.is_fragmenting_payload(),
+                        len_source: LenSource::Ipv4HeaderTotalLen,
+                        payload: &payload
+                    }
+                );
+            }
+
+            // error len smaller then min header len
+            for len in 1..Ipv4Header::MIN_LEN {
+                assert_eq!(
+                    LaxIpv4Slice::from_slice(&buffer[..len]),
+                    Err(E::Len(err::LenError {
+                        required_len: Ipv4Header::MIN_LEN,
+                        len,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::Ipv4Header,
+                        layer_start_offset: 0,
+                    }))
+                );
+            }
+
+            // ihl value error
+            {
+                let mut bad_ihl_buffer = buffer.clone();
+                for bad_ihl in 0..5 {
+                    bad_ihl_buffer[0] = (bad_ihl_buffer[0] & 0xf0) | bad_ihl;
+                    assert_eq!(
+                        LaxIpv4Slice::from_slice(&bad_ihl_buffer),
+                        Err(E::Content(HeaderLengthSmallerThanHeader { ihl: bad_ihl }))
+                    );
+                }
+            }
+
+            // ihl len error
+            for short_ihl in 5..usize::from(v4.ihl()) {
+                assert_eq!(
+                    LaxIpv4Slice::from_slice(&buffer[..4*short_ihl]),
+                    Err(E::Len(err::LenError {
+                        required_len: usize::from(v4.ihl())*4,
+                        len: 4*short_ihl,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::Ipv4Header,
+                        layer_start_offset: 0,
+                    }))
+                );
+            }
+
+            // total_len bigger then slice len (fallback to slice len)
+            for payload_len in 0..payload.len(){
+                let (actual, stop_err) = LaxIpv4Slice::from_slice(&buffer[..v4.header_len() + v4_exts.header_len() + payload_len]).unwrap();
+                assert_eq!(stop_err, None);
+                assert_eq!(&actual.header().to_header(), header.ipv4().unwrap().0);
+                assert_eq!(
+                    actual.payload(),
+                    &LaxIpPayloadSlice{
+                        incomplete: true,
+                        ip_number: header.next_header().unwrap(),
+                        fragmented: header.is_fragmenting_payload(),
+                        len_source: LenSource::Slice,
+                        payload: &payload[..payload_len]
+                    }
+                );
+            }
+
+            // len error ipv4 extensions
+            if v4_exts.header_len() > 0 {
+                let (actual, stop_err) = LaxIpv4Slice::from_slice(&buffer[..v4.header_len() + 1]).unwrap();
+                assert_eq!(&actual.header().to_header(), header.ipv4().unwrap().0);
+                assert_eq!(
+                    actual.payload(),
+                    &LaxIpPayloadSlice{
+                        incomplete: true,
+                        ip_number: AUTH,
+                        fragmented: header.is_fragmenting_payload(),
+                        len_source: LenSource::Slice,
+                        payload: &buffer[v4.header_len()..v4.header_len() + 1]
+                    }
+                );
+                assert_eq!(stop_err, Some(A::Len(LenError{
+                    required_len: IpAuthHeader::MIN_LEN,
+                    len: 1,
+                    len_source: LenSource::Slice,
+                    layer: err::Layer::IpAuthHeader,
+                    layer_start_offset: header.ipv4().unwrap().0.header_len()
+                })));
+            }
+
+            // content error ipv4 extensions
+            if v4_exts.auth.is_some() {
+                use err::ip_auth::HeaderError::ZeroPayloadLen;
+
+
+                // introduce a auth header zero payload error
+                let mut errored_buffer = buffer.clone();
+                // inject length zero into auth header (not valid, will
+                // trigger a content error)
+                errored_buffer[v4.header_len() + 1] = 0;
+
+                let (actual, stop_err) = LaxIpv4Slice::from_slice(&errored_buffer).unwrap();
+                assert_eq!(&actual.header().to_header(), header.ipv4().unwrap().0);
+                assert!(actual.extensions().is_empty());
+                let auth_offset = header.ipv4().unwrap().0.header_len();
+                let payload_end = auth_offset + v4_exts.auth.map(|v| v.header_len()).unwrap() + payload.len();
+                assert_eq!(
+                    actual.payload(),
+                    &LaxIpPayloadSlice{
+                        incomplete: false,
+                        ip_number: AUTH,
+                        fragmented: header.is_fragmenting_payload(),
+                        len_source: LenSource::Ipv4HeaderTotalLen,
+                        payload: &errored_buffer[auth_offset..payload_end]
+                    }
+                );
+                assert_eq!(stop_err, Some(A::Content(ZeroPayloadLen)));
+            }
+
+            // total length smaller the header (fallback to slice len)
+            {
+                let bad_total_len = (v4.header_len() - 1) as u16;
+
+                let mut buffer = buffer.clone();
+                // inject bad total_len
+                let bad_total_len_be = bad_total_len.to_be_bytes();
+                buffer[2] = bad_total_len_be[0];
+                buffer[3] = bad_total_len_be[1];
+
+                let (actual, actual_stop_error) = LaxIpv4Slice::from_slice(&buffer[..]).unwrap();
+                assert_eq!(actual_stop_error, None);
+
+                let (v4_header, v4_exts) = header.ipv4().unwrap();
+                let expected_headers = IpHeaders::Ipv4(
+                    {
+                        let mut expected_v4 = v4_header.clone();
+                        expected_v4.total_len = bad_total_len;
+                        expected_v4
+                    },
+                    v4_exts.clone()
+                );
+                assert_eq!(expected_headers.ipv4().unwrap().0, &actual.header().to_header());
+                assert_eq!(
+                    actual.payload(),
+                    &LaxIpPayloadSlice{
+                        incomplete: false,
+                        ip_number: header.next_header().unwrap(),
+                        fragmented: header.is_fragmenting_payload(),
+                        len_source: LenSource::Slice,
+                        payload: &buffer[v4_header.header_len() + v4_exts.header_len()..],
+                    }
+                );
+            }
+        }
+    }
+
+    #[test]
+    fn is_payload_fragmented() {
+        use crate::ip_number::UDP;
+        // non-fragmented
+        {
+            let payload: [u8; 6] = [1, 2, 3, 4, 5, 6];
+            let ipv4 =
+                Ipv4Header::new(payload.len() as u16, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10]).unwrap();
+            let data = {
+                let mut data = Vec::with_capacity(ipv4.header_len() + payload.len());
+                data.extend_from_slice(&ipv4.to_bytes());
+                data.extend_from_slice(&payload);
+                data
+            };
+
+            let (slice, stop_err) = LaxIpv4Slice::from_slice(&data).unwrap();
+            assert_eq!(None, stop_err);
+            assert!(false == slice.is_payload_fragmented());
+        }
+        // fragmented
+        {
+            let payload: [u8; 6] = [1, 2, 3, 4, 5, 6];
+            let mut ipv4 =
+                Ipv4Header::new(payload.len() as u16, 1, UDP, [3, 4, 5, 6], [7, 8, 9, 10]).unwrap();
+            ipv4.fragment_offset = 123.try_into().unwrap();
+            let data = {
+                let mut data = Vec::with_capacity(ipv4.header_len() + payload.len());
+                data.extend_from_slice(&ipv4.to_bytes());
+                data.extend_from_slice(&payload);
+                data
+            };
+
+            let (slice, stop_err) = LaxIpv4Slice::from_slice(&data).unwrap();
+            assert_eq!(None, stop_err);
+            assert!(slice.is_payload_fragmented());
+        }
+    }
+}
diff --git a/src/net/lax_ipv6_slice.rs b/src/net/lax_ipv6_slice.rs
new file mode 100644
index 0000000..e0b08f5
--- /dev/null
+++ b/src/net/lax_ipv6_slice.rs
@@ -0,0 +1,574 @@
+use crate::{
+    err::{ipv6, ipv6_exts},
+    *,
+};
+
+/// Slice containing laxly separated IPv6 headers & payload.
+///
+/// Compared to the normal [`Ipv6Slice`] this slice allows the
+/// payload to incomplete/cut off and errors to be present in
+/// the IpPayload.
+///
+/// The main usecases 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.
+/// * Parsing packets where the `total_len` (for IPv4) have not yet been set.
+///   This can be useful when parsing packets which have been recorded in a
+///   layer before the length field was set (e.g. before the operating
+///   system set the length fields).
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct LaxIpv6Slice<'a> {
+    pub(crate) header: Ipv6HeaderSlice<'a>,
+    pub(crate) exts: Ipv6ExtensionsSlice<'a>,
+    pub(crate) payload: LaxIpPayloadSlice<'a>,
+}
+
+impl<'a> LaxIpv6Slice<'a> {
+    /// Seperate 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:
+    ///
+    /// * 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.
+    /// * Parsing packets where the `payload_length` (in the IPv6 header) has not
+    ///   yet been set. This can be useful when parsing packets which have been
+    ///   recorded in a layer before the length field was set (e.g. before the operating
+    ///   system set the length fields).
+    ///
+    /// # Differences to `from_slice`:
+    ///
+    /// There are two main differences:
+    ///
+    /// * Errors in the expansion headers will only stop the parsing and return an `Ok`
+    ///   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.
+    /// * `LaxIpv4Slice::from_slice` ignores inconsistent `payload_length` values. When the
+    ///   `payload_length` value in the IPv6 header is inconsistant 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
+    /// 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`
+    /// field in the IPv6 header is
+    ///
+    /// * Bigger then the given slice (payload cannot fully be seperated).
+    /// * The value `0`.
+    pub fn from_slice(
+        slice: &'a [u8],
+    ) -> Result<
+        (
+            LaxIpv6Slice<'a>,
+            Option<(ipv6_exts::HeaderSliceError, err::Layer)>,
+        ),
+        ipv6::HeaderSliceError,
+    > {
+        // try reading the header
+        let header = Ipv6HeaderSlice::from_slice(slice)?;
+
+        // restrict slice by the length specified in the header
+        let (header_payload, len_source, incomplete) =
+            if 0 == header.payload_length() && slice.len() > Ipv6Header::LEN {
+                // In case the payload_length is 0 assume that the entire
+                // rest of the slice is part of the packet until the jumbogram
+                // parameters can be parsed.
+
+                // TODO: Add payload length parsing from the jumbogram
+                (
+                    unsafe {
+                        core::slice::from_raw_parts(
+                            slice.as_ptr().add(Ipv6Header::LEN),
+                            slice.len() - Ipv6Header::LEN,
+                        )
+                    },
+                    LenSource::Slice,
+                    false,
+                )
+            } else {
+                let payload_len = usize::from(header.payload_length());
+                let expected_len = Ipv6Header::LEN + payload_len;
+                if slice.len() < expected_len {
+                    (
+                        unsafe {
+                            core::slice::from_raw_parts(
+                                slice.as_ptr().add(Ipv6Header::LEN),
+                                slice.len() - Ipv6Header::LEN,
+                            )
+                        },
+                        LenSource::Slice,
+                        true,
+                    )
+                } else {
+                    (
+                        unsafe {
+                            core::slice::from_raw_parts(
+                                slice.as_ptr().add(Ipv6Header::LEN),
+                                payload_len,
+                            )
+                        },
+                        LenSource::Ipv6HeaderPayloadLen,
+                        false,
+                    )
+                }
+            };
+
+        // parse extension headers
+        let (exts, payload_ip_number, payload, mut ext_stop_err) =
+            Ipv6ExtensionsSlice::from_slice_lax(header.next_header(), header_payload);
+
+        // modify length errors
+        if let Some((ipv6_exts::HeaderSliceError::Len(err), _)) = &mut ext_stop_err {
+            err.len_source = len_source;
+            err.layer_start_offset += Ipv6Header::LEN;
+        };
+
+        let fragmented = exts.is_fragmenting_payload();
+        Ok((
+            LaxIpv6Slice {
+                header,
+                exts,
+                payload: LaxIpPayloadSlice {
+                    incomplete,
+                    ip_number: payload_ip_number,
+                    fragmented,
+                    len_source,
+                    payload,
+                },
+            },
+            ext_stop_err,
+        ))
+    }
+
+    /// Returns a slice containing the IPv6 header.
+    #[inline]
+    pub fn header(&self) -> Ipv6HeaderSlice<'a> {
+        self.header
+    }
+
+    /// Returns a slice containing the IPv6 extension headers.
+    #[inline]
+    pub fn extensions(&self) -> &Ipv6ExtensionsSlice<'a> {
+        &self.exts
+    }
+
+    /// Returns a slice containing the data after the IPv6 header
+    /// and IPv6 extensions headers.
+    #[inline]
+    pub fn payload(&self) -> &LaxIpPayloadSlice<'a> {
+        &self.payload
+    }
+
+    /// Returns true if the payload is flagged as being fragmented.
+    #[inline]
+    pub fn is_payload_fragmented(&self) -> bool {
+        self.payload.fragmented
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::{
+        err::{Layer, LenError},
+        ip_number::{AUTH, IGMP, UDP},
+        test_gens::*,
+    };
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn debug_clone_eq(
+            ipv6_base in ipv6_any(),
+            auth_base in ip_auth_any()
+        ) {
+            let mut auth = auth_base.clone();
+            auth.next_header = IGMP;
+            let payload: [u8;4] = [1,2,3,4];
+            let mut data = Vec::with_capacity(
+                ipv6_base.header_len() +
+                auth.header_len() +
+                payload.len()
+            );
+            let mut ipv6 = ipv6_base.clone();
+            ipv6.next_header = AUTH;
+            ipv6.payload_length = (auth.header_len() + payload.len()) as u16;
+            data.extend_from_slice(&ipv6.to_bytes());
+            data.extend_from_slice(&auth.to_bytes());
+            data.extend_from_slice(&payload);
+
+            // decode packet
+            let (slice, _) = LaxIpv6Slice::from_slice(&data).unwrap();
+
+            // check debug output
+            prop_assert_eq!(
+                format!("{:?}", slice),
+                format!(
+                    "LaxIpv6Slice {{ header: {:?}, exts: {:?}, payload: {:?} }}",
+                    slice.header(),
+                    slice.extensions(),
+                    slice.payload()
+                )
+            );
+            prop_assert_eq!(slice.clone(), slice);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            ipv6_base in ipv6_any(),
+            auth_base in ip_auth_any()
+        ) {
+            let payload: [u8;6] = [1,2,3,4,5,6];
+
+            // build packets
+            let data_without_ext = {
+                let mut data = Vec::with_capacity(
+                    ipv6_base.header_len() +
+                    payload.len() +
+                    4
+                );
+                let mut ipv6 = ipv6_base.clone();
+                ipv6.payload_length = (payload.len()) as u16;
+                ipv6.next_header = UDP;
+                data.extend_from_slice(&ipv6.to_bytes());
+                data.extend_from_slice(&payload);
+                data.extend_from_slice(&[0,0,0,0]);
+                data
+            };
+            let data_with_ext = {
+                let payload: [u8;6] = [1,2,3,4,5,6];
+                let mut data = Vec::with_capacity(
+                    ipv6_base.header_len() +
+                    auth_base.header_len() +
+                    payload.len() +
+                    4
+                );
+                let mut ipv6 = ipv6_base.clone();
+                ipv6.payload_length = (auth_base.header_len() + payload.len()) as u16;
+                ipv6.next_header = AUTH;
+                let mut auth = auth_base.clone();
+                auth.next_header = UDP;
+                data.extend_from_slice(&ipv6.to_bytes());
+                data.extend_from_slice(&auth.to_bytes());
+                data.extend_from_slice(&payload);
+                data.extend_from_slice(&[0,0,0,0]);
+                data
+            };
+
+            // parsing without extensions (normal length)
+            {
+                let (actual, actual_stop_err) = LaxIpv6Slice::from_slice(&data_without_ext).unwrap();
+                prop_assert_eq!(None, actual_stop_err);
+                prop_assert_eq!(actual.header().slice(), &data_without_ext[..ipv6_base.header_len()]);
+                prop_assert!(actual.extensions().first_header().is_none());
+                prop_assert_eq!(
+                    actual.payload(),
+                    &LaxIpPayloadSlice{
+                        incomplete: false,
+                        ip_number: UDP.into(),
+                        fragmented: false,
+                        len_source: LenSource::Ipv6HeaderPayloadLen,
+                        payload: &payload,
+                    }
+                );
+            }
+
+            // parsing with extensions (normal length)
+            {
+                let (actual, actual_stop_err) = LaxIpv6Slice::from_slice(&data_with_ext).unwrap();
+                prop_assert_eq!(None, actual_stop_err);
+                prop_assert_eq!(actual.header().slice(), &data_with_ext[..ipv6_base.header_len()]);
+                let (expected, _, _) = Ipv6ExtensionsSlice::from_slice(AUTH, &data_with_ext[ipv6_base.header_len()..]).unwrap();
+                prop_assert_eq!(
+                    actual.extensions(),
+                    &expected
+                );
+                prop_assert_eq!(
+                    actual.payload(),
+                    &LaxIpPayloadSlice{
+                        incomplete: false,
+                        ip_number: UDP.into(),
+                        fragmented: false,
+                        len_source: LenSource::Ipv6HeaderPayloadLen,
+                        payload: &payload,
+                    }
+                );
+            }
+
+            // parsing without extensions (zero length, fallback to slice length)
+            {
+                // inject zero as payload length
+                let mut data = data_without_ext.clone();
+                data[4] = 0;
+                data[5] = 0;
+                let (actual, actual_stop_err) = LaxIpv6Slice::from_slice(&data).unwrap();
+                prop_assert_eq!(None, actual_stop_err);
+                prop_assert_eq!(actual.header().slice(), &data[..ipv6_base.header_len()]);
+                prop_assert!(actual.extensions().first_header().is_none());
+                prop_assert_eq!(
+                    actual.payload(),
+                    &LaxIpPayloadSlice{
+                        incomplete: false,
+                        ip_number: UDP.into(),
+                        fragmented: false,
+                        len_source: LenSource::Slice,
+                        payload: &data[ipv6_base.header_len()..],
+                    }
+                );
+            }
+
+            // parsing with extensions (zero length, fallback to slice length)
+            {
+                // inject zero as payload length
+                let mut data = data_with_ext.clone();
+                data[4] = 0;
+                data[5] = 0;
+                let (actual, actual_stop_err) = LaxIpv6Slice::from_slice(&data).unwrap();
+                prop_assert_eq!(None, actual_stop_err);
+                prop_assert_eq!(actual.header().slice(), &data[..ipv6_base.header_len()]);
+                let (expected, _, _) = Ipv6ExtensionsSlice::from_slice(AUTH, &data[ipv6_base.header_len()..]).unwrap();
+                prop_assert_eq!(
+                    actual.extensions(),
+                    &expected
+                );
+                prop_assert_eq!(
+                    actual.payload(),
+                    &LaxIpPayloadSlice{
+                        incomplete: false,
+                        ip_number: UDP.into(),
+                        fragmented: false,
+                        len_source: LenSource::Slice,
+                        payload: &data[ipv6_base.header_len() + auth_base.header_len()..],
+                    }
+                );
+            }
+
+            // header content error
+            {
+                use crate::err::ipv6::HeaderError;
+                // inject invalid ip version
+                let mut data = data_without_ext.clone();
+                data[0] = data[0] & 0x0f; // version 0
+                prop_assert_eq!(
+                    LaxIpv6Slice::from_slice(&data).unwrap_err(),
+                    ipv6::HeaderSliceError::Content(
+                        HeaderError::UnexpectedVersion{ version_number: 0 }
+                    )
+                );
+            }
+
+            // header length error
+            for len in 0..Ipv6Header::LEN {
+                prop_assert_eq!(
+                    LaxIpv6Slice::from_slice(&data_without_ext[..len]).unwrap_err(),
+                    ipv6::HeaderSliceError::Len(
+                        LenError{
+                            required_len: Ipv6Header::LEN,
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: Layer::Ipv6Header,
+                            layer_start_offset: 0
+                        }
+                    )
+                );
+            }
+
+            // payload length larger then slice (fallback to slice length)
+            {
+                let len = ipv6_base.header_len() + payload.len() - 1;
+                let (actual , actual_stop_err) = LaxIpv6Slice::from_slice(&data_without_ext[..len]).unwrap();
+                prop_assert_eq!(actual_stop_err, None);
+                prop_assert_eq!(actual.header().slice(), &data_without_ext[..ipv6_base.header_len()]);
+                prop_assert_eq!(
+                    0,
+                    actual.extensions().slice().len()
+                );
+                prop_assert_eq!(
+                    actual.payload(),
+                    &LaxIpPayloadSlice{
+                        incomplete: true,
+                        ip_number: UDP.into(),
+                        fragmented: false,
+                        len_source: LenSource::Slice,
+                        payload: &data_without_ext[ipv6_base.header_len()..len],
+                    }
+                );
+            }
+
+            // payload length error auth header
+            {
+                use crate::err::{LenError, Layer};
+
+                let required_len = ipv6_base.header_len() + auth_base.header_len();
+                let (actual, actual_stop_err) = LaxIpv6Slice::from_slice(&data_with_ext[..required_len - 1]).unwrap();
+                prop_assert_eq!(
+                    actual_stop_err.unwrap(),
+                    (
+                        ipv6_exts::HeaderSliceError::Len(LenError{
+                            required_len: required_len - Ipv6Header::LEN,
+                            len: required_len - Ipv6Header::LEN - 1,
+                            len_source: LenSource::Slice,
+                            layer: Layer::IpAuthHeader,
+                            layer_start_offset: Ipv6Header::LEN,
+                        }),
+                        err::Layer::IpAuthHeader
+                    )
+                );
+                prop_assert_eq!(actual.header().slice(), &data_with_ext[..ipv6_base.header_len()]);
+                prop_assert_eq!(
+                    actual.payload(),
+                    &LaxIpPayloadSlice{
+                        incomplete: true,
+                        ip_number: AUTH,
+                        fragmented: false,
+                        len_source: LenSource::Slice,
+                        payload: &data_with_ext[ipv6_base.header_len()..required_len - 1],
+                    }
+                );
+            }
+
+            // auth length error
+            {
+                use crate::err::{LenError, Layer};
+
+                // inject payload length that is smaller then the auth header
+                let mut data = data_with_ext.clone();
+                let payload_len_too_small = auth_base.header_len() - 1;
+                {
+                    let plts = (payload_len_too_small as u16).to_be_bytes();
+                    data[4] = plts[0];
+                    data[5] = plts[1];
+                }
+
+                let (actual, actual_stop_err) = LaxIpv6Slice::from_slice(&data).unwrap();
+                prop_assert_eq!(
+                    actual_stop_err.unwrap(),
+                    (
+                        ipv6_exts::HeaderSliceError::Len(
+                            LenError{
+                                required_len: auth_base.header_len(),
+                                len: auth_base.header_len() - 1,
+                                len_source: LenSource::Ipv6HeaderPayloadLen,
+                                layer: Layer::IpAuthHeader,
+                                layer_start_offset: ipv6_base.header_len(),
+                            }
+                        ),
+                        err::Layer::IpAuthHeader
+                    )
+                );
+                prop_assert_eq!(actual.header().slice(), &data[..ipv6_base.header_len()]);
+                prop_assert_eq!(
+                    actual.payload(),
+                    &LaxIpPayloadSlice{
+                        incomplete: false,
+                        ip_number: AUTH,
+                        fragmented: false,
+                        len_source: LenSource::Ipv6HeaderPayloadLen,
+                        payload: &data[ipv6_base.header_len()..ipv6_base.header_len() + payload_len_too_small],
+                    }
+                );
+            }
+
+            // auth content error
+            {
+                use crate::err::{ip_auth, ipv6_exts};
+
+                // inject zero as auth header length
+                let mut data = data_with_ext.clone();
+                data[ipv6_base.header_len() + 1] = 0;
+
+                let (actual, actual_stop_error) = LaxIpv6Slice::from_slice(&data).unwrap();
+
+                prop_assert_eq!(
+                    actual_stop_error.unwrap(),
+                    (
+                        ipv6_exts::HeaderSliceError::Content(ipv6_exts::HeaderError::IpAuth(
+                            ip_auth::HeaderError::ZeroPayloadLen
+                        )),
+                        err::Layer::IpAuthHeader
+                    )
+                );
+                prop_assert_eq!(
+                    actual.header().slice(),
+                    &data[..ipv6_base.header_len()]
+                );
+                prop_assert_eq!(
+                    actual.payload(),
+                    &LaxIpPayloadSlice{
+                        incomplete: false,
+                        ip_number: AUTH,
+                        fragmented: false,
+                        len_source: LenSource::Ipv6HeaderPayloadLen,
+                        payload: &data[ipv6_base.header_len()..ipv6_base.header_len() + auth_base.header_len() + payload.len()],
+                    }
+                );
+            }
+        }
+    }
+
+    #[test]
+    fn is_payload_fragmented() {
+        use crate::ip_number::{IPV6_FRAG, UDP};
+
+        // not fragmented
+        {
+            let data = Ipv6Header {
+                traffic_class: 0,
+                flow_label: 1.try_into().unwrap(),
+                payload_length: 0,
+                next_header: UDP,
+                hop_limit: 4,
+                source: [0; 16],
+                destination: [0; 16],
+            }
+            .to_bytes();
+            assert_eq!(
+                false,
+                LaxIpv6Slice::from_slice(&data)
+                    .unwrap()
+                    .0
+                    .is_payload_fragmented()
+            );
+        }
+
+        // fragmented
+        {
+            let ipv6_frag = Ipv6FragmentHeader {
+                next_header: UDP,
+                fragment_offset: 0.try_into().unwrap(),
+                more_fragments: true,
+                identification: 0,
+            };
+            let ipv6 = Ipv6Header {
+                traffic_class: 0,
+                flow_label: 1.try_into().unwrap(),
+                payload_length: ipv6_frag.header_len() as u16,
+                next_header: IPV6_FRAG,
+                hop_limit: 4,
+                source: [0; 16],
+                destination: [0; 16],
+            };
+
+            let mut data = Vec::with_capacity(ipv6.header_len() + ipv6_frag.header_len());
+            data.extend_from_slice(&ipv6.to_bytes());
+            data.extend_from_slice(&ipv6_frag.to_bytes());
+            assert!(Ipv6Slice::from_slice(&data)
+                .unwrap()
+                .is_payload_fragmented());
+        }
+    }
+}
diff --git a/src/net/lax_net_slice.rs b/src/net/lax_net_slice.rs
new file mode 100644
index 0000000..69523f2
--- /dev/null
+++ b/src/net/lax_net_slice.rs
@@ -0,0 +1,212 @@
+use crate::*;
+
+/// Slice containing laxly parsed the network headers & payloads (e.g. IPv4, IPv6, ARP).
+///
+/// Compared to the normal [`NetSlice`] this slice allows the
+/// payload to be incomplete/cut off and errors to be present in
+/// the IpPayload.
+///
+/// The main usecases 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.
+/// * Parsing packets where the `total_len` (for IPv4) or `payload_len` (for IPv6)
+///   have not yet been set. This can be useful when parsing packets which have
+///   been recorded in a layer before the length field was set (e.g. before the
+///   operating system set the length fields).
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum LaxNetSlice<'a> {
+    /// The ipv4 header & the decoded extension headers.
+    Ipv4(LaxIpv4Slice<'a>),
+    /// The ipv6 header & the decoded extension headers.
+    Ipv6(LaxIpv6Slice<'a>),
+}
+
+impl<'a> LaxNetSlice<'a> {
+    /// Returns a reference to ip payload if the net slice contains
+    /// an ipv4 or ipv6 slice.
+    #[inline]
+    pub fn ip_payload_ref(&self) -> Option<&LaxIpPayloadSlice<'a>> {
+        match self {
+            LaxNetSlice::Ipv4(s) => Some(&s.payload),
+            LaxNetSlice::Ipv6(s) => Some(&s.payload),
+        }
+    }
+}
+
+impl<'a> From<LaxIpSlice<'a>> for LaxNetSlice<'a> {
+    #[inline]
+    fn from(value: LaxIpSlice<'a>) -> LaxNetSlice<'a> {
+        match value {
+            LaxIpSlice::Ipv4(ipv4) => LaxNetSlice::Ipv4(ipv4),
+            LaxIpSlice::Ipv6(ipv6) => LaxNetSlice::Ipv6(ipv6),
+        }
+    }
+}
+
+impl<'a> From<LaxIpv4Slice<'a>> for LaxNetSlice<'a> {
+    #[inline]
+    fn from(value: LaxIpv4Slice<'a>) -> LaxNetSlice<'a> {
+        LaxNetSlice::Ipv4(value)
+    }
+}
+
+impl<'a> From<LaxIpv6Slice<'a>> for LaxNetSlice<'a> {
+    #[inline]
+    fn from(value: LaxIpv6Slice<'a>) -> LaxNetSlice<'a> {
+        LaxNetSlice::Ipv6(value)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::*;
+    use alloc::{format, vec::Vec};
+
+    #[test]
+    fn debug() {
+        let bytes = Ipv6Header {
+            next_header: IpNumber::UDP,
+            ..Default::default()
+        }
+        .to_bytes();
+        let s = LaxIpv6Slice::from_slice(&bytes).unwrap().0;
+        let n = LaxNetSlice::Ipv6(s.clone());
+        assert_eq!(format!("{n:?}"), format!("Ipv6({s:?})"));
+    }
+
+    #[test]
+    fn clone_eq() {
+        let bytes = Ipv6Header {
+            next_header: IpNumber::UDP,
+            ..Default::default()
+        }
+        .to_bytes();
+        let s = LaxNetSlice::Ipv6(LaxIpv6Slice::from_slice(&bytes).unwrap().0);
+        assert_eq!(s, s.clone())
+    }
+
+    #[test]
+    fn ip_payload_ref() {
+        // ipv4
+        {
+            let payload = [1, 2, 3, 4];
+            let bytes = {
+                let mut bytes = Vec::with_capacity(Ipv4Header::MIN_LEN + 4);
+                bytes.extend_from_slice(
+                    &(Ipv4Header {
+                        total_len: Ipv4Header::MIN_LEN_U16 + 4,
+                        protocol: IpNumber::UDP,
+                        ..Default::default()
+                    })
+                    .to_bytes(),
+                );
+                bytes.extend_from_slice(&payload);
+                bytes
+            };
+            let s = LaxNetSlice::Ipv4(LaxIpv4Slice::from_slice(&bytes).unwrap().0);
+            assert_eq!(
+                s.ip_payload_ref(),
+                Some(&LaxIpPayloadSlice {
+                    ip_number: IpNumber::UDP,
+                    fragmented: false,
+                    len_source: LenSource::Ipv4HeaderTotalLen,
+                    payload: &payload,
+                    incomplete: false,
+                })
+            );
+        }
+        // ipv6
+        {
+            let payload = [1, 2, 3, 4];
+            let bytes = {
+                let mut bytes = Vec::with_capacity(Ipv6Header::LEN + 4);
+                bytes.extend_from_slice(
+                    &(Ipv6Header {
+                        next_header: IpNumber::UDP,
+                        payload_length: 4,
+                        ..Default::default()
+                    })
+                    .to_bytes(),
+                );
+                bytes.extend_from_slice(&payload);
+                bytes
+            };
+            let s = LaxNetSlice::Ipv6(LaxIpv6Slice::from_slice(&bytes).unwrap().0);
+            assert_eq!(
+                s.ip_payload_ref(),
+                Some(&LaxIpPayloadSlice {
+                    ip_number: IpNumber::UDP,
+                    fragmented: false,
+                    len_source: LenSource::Ipv6HeaderPayloadLen,
+                    payload: &payload,
+                    incomplete: false,
+                })
+            );
+        }
+    }
+
+    #[test]
+    fn from() {
+        // IpSlice::Ipv4
+        {
+            let payload = [1, 2, 3, 4];
+            let bytes = {
+                let mut bytes = Vec::with_capacity(Ipv4Header::MIN_LEN + 4);
+                bytes.extend_from_slice(
+                    &(Ipv4Header {
+                        total_len: Ipv4Header::MIN_LEN_U16 + 4,
+                        protocol: IpNumber::UDP,
+                        ..Default::default()
+                    })
+                    .to_bytes(),
+                );
+                bytes.extend_from_slice(&payload);
+                bytes
+            };
+            let i = LaxIpv4Slice::from_slice(&bytes).unwrap().0;
+            let actual: LaxNetSlice = LaxIpSlice::Ipv4(i.clone()).into();
+            assert_eq!(LaxNetSlice::Ipv4(i.clone()), actual);
+        }
+        // LaxIpv4Slice
+        {
+            let payload = [1, 2, 3, 4];
+            let bytes = {
+                let mut bytes = Vec::with_capacity(Ipv4Header::MIN_LEN + 4);
+                bytes.extend_from_slice(
+                    &(Ipv4Header {
+                        total_len: Ipv4Header::MIN_LEN_U16 + 4,
+                        protocol: IpNumber::UDP,
+                        ..Default::default()
+                    })
+                    .to_bytes(),
+                );
+                bytes.extend_from_slice(&payload);
+                bytes
+            };
+            let i = LaxIpv4Slice::from_slice(&bytes).unwrap().0;
+            let actual: LaxNetSlice = i.clone().into();
+            assert_eq!(LaxNetSlice::Ipv4(i.clone()), actual);
+        }
+        // IpSlice::Ipv6
+        {
+            let payload = [1, 2, 3, 4];
+            let bytes = {
+                let mut bytes = Vec::with_capacity(Ipv6Header::LEN + 4);
+                bytes.extend_from_slice(
+                    &(Ipv6Header {
+                        next_header: IpNumber::UDP,
+                        payload_length: 4,
+                        ..Default::default()
+                    })
+                    .to_bytes(),
+                );
+                bytes.extend_from_slice(&payload);
+                bytes
+            };
+            let i = LaxIpv6Slice::from_slice(&bytes).unwrap().0;
+            let actual: LaxNetSlice = i.clone().into();
+            assert_eq!(LaxNetSlice::Ipv6(i.clone()), actual);
+        }
+    }
+}
diff --git a/src/net/mod.rs b/src/net/mod.rs
new file mode 100644
index 0000000..cee996f
--- /dev/null
+++ b/src/net/mod.rs
@@ -0,0 +1,104 @@
+mod ip_auth_header;
+pub use ip_auth_header::*;
+
+mod ip_auth_header_slice;
+pub use ip_auth_header_slice::*;
+
+mod ip_frag_offset;
+pub use ip_frag_offset::*;
+
+mod ip_headers;
+pub use ip_headers::*;
+
+mod ip_number_impl;
+pub use ip_number_impl::*;
+
+mod ip_payload_slice;
+pub use ip_payload_slice::*;
+
+mod ip_slice;
+pub use ip_slice::*;
+
+mod ipv4_dscp;
+pub use ipv4_dscp::*;
+
+mod ipv4_ecn;
+pub use ipv4_ecn::*;
+
+mod ipv4_exts;
+pub use ipv4_exts::*;
+
+mod ipv4_exts_slice;
+pub use ipv4_exts_slice::*;
+
+mod ipv4_header;
+pub use ipv4_header::*;
+
+mod ipv4_header_slice;
+pub use ipv4_header_slice::*;
+
+mod ipv4_options;
+pub use ipv4_options::*;
+
+mod ipv4_slice;
+pub use ipv4_slice::*;
+
+mod ipv6_ext_slice;
+pub use ipv6_ext_slice::*;
+
+mod ipv6_ext_slice_iter;
+pub use ipv6_ext_slice_iter::*;
+
+mod ipv6_exts;
+pub use ipv6_exts::*;
+
+mod ipv6_exts_slice;
+pub use ipv6_exts_slice::*;
+
+mod ipv6_flow_label;
+pub use ipv6_flow_label::*;
+
+mod ipv6_fragment_header;
+pub use ipv6_fragment_header::*;
+
+mod ipv6_fragment_header_slice;
+pub use ipv6_fragment_header_slice::*;
+
+mod ipv6_header;
+pub use ipv6_header::*;
+
+mod ipv6_header_slice;
+pub use ipv6_header_slice::*;
+
+mod ipv6_raw_ext_header;
+pub use ipv6_raw_ext_header::*;
+
+mod ipv6_raw_ext_header_slice;
+pub use ipv6_raw_ext_header_slice::*;
+
+mod ipv6_routing_exts;
+pub use ipv6_routing_exts::*;
+
+mod ipv6_slice;
+pub use ipv6_slice::*;
+
+mod lax_ip_payload_slice;
+pub use lax_ip_payload_slice::*;
+
+mod lax_ip_slice;
+pub use lax_ip_slice::*;
+
+mod lax_ipv4_slice;
+pub use lax_ipv4_slice::*;
+
+mod lax_ipv6_slice;
+pub use lax_ipv6_slice::*;
+
+mod lax_net_slice;
+pub use lax_net_slice::*;
+
+mod net_headers;
+pub use net_headers::*;
+
+mod net_slice;
+pub use net_slice::*;
diff --git a/src/net/net_headers.rs b/src/net/net_headers.rs
new file mode 100644
index 0000000..f2af1f4
--- /dev/null
+++ b/src/net/net_headers.rs
@@ -0,0 +1,152 @@
+use crate::*;
+
+/// Deprecated use [`crate::NetHeaders`] instead.
+#[deprecated(since = "0.14.0", note = "`IpHeader` was renamed to `NetHeaders`")]
+pub type IpHeader = NetHeaders;
+
+/// Headers on the network layer (e.g. IP, ARP, ...).
+#[derive(Clone, Debug, Eq, PartialEq)]
+#[allow(clippy::large_enum_variant)]
+pub enum NetHeaders {
+    /// IPv4 header & extension headers.
+    Ipv4(Ipv4Header, Ipv4Extensions),
+    /// IPv6 header & extension headers.
+    Ipv6(Ipv6Header, Ipv6Extensions),
+}
+
+impl NetHeaders {
+    /// Returns references to the IPv4 header & extensions if the header contains IPv4 values.
+    pub fn ipv4_ref(&self) -> Option<(&Ipv4Header, &Ipv4Extensions)> {
+        if let NetHeaders::Ipv4(header, exts) = self {
+            Some((header, exts))
+        } else {
+            None
+        }
+    }
+
+    /// Returns references to the IPv6 header & extensions if the header contains IPv6 values.
+    pub fn ipv6_ref(&self) -> Option<(&Ipv6Header, &Ipv6Extensions)> {
+        if let NetHeaders::Ipv6(header, exts) = self {
+            Some((header, exts))
+        } else {
+            None
+        }
+    }
+
+    /// Returns the size when the header & extension headers are serialized
+    pub fn header_len(&self) -> usize {
+        use crate::NetHeaders::*;
+        match *self {
+            Ipv4(ref header, ref extensions) => header.header_len() + extensions.header_len(),
+            Ipv6(_, ref extensions) => Ipv6Header::LEN + extensions.header_len(),
+        }
+    }
+}
+
+impl From<IpHeaders> for NetHeaders {
+    #[inline]
+    fn from(value: IpHeaders) -> Self {
+        match value {
+            IpHeaders::Ipv4(h, e) => NetHeaders::Ipv4(h, e),
+            IpHeaders::Ipv6(h, e) => NetHeaders::Ipv6(h, e),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::*;
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        let h = Ipv4Header {
+            ..Default::default()
+        };
+        let e = Ipv4Extensions {
+            ..Default::default()
+        };
+        let n = NetHeaders::Ipv4(h.clone(), e.clone());
+        assert_eq!(format!("{n:?}"), format!("Ipv4({h:?}, {e:?})"));
+    }
+
+    #[test]
+    fn clone_eq() {
+        let n = NetHeaders::Ipv4(Default::default(), Default::default());
+        assert_eq!(n, n.clone())
+    }
+
+    #[test]
+    fn ipv4_ref() {
+        // 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)));
+        }
+        // ipv6
+        {
+            let h: Ipv6Header = Default::default();
+            let e: Ipv6Extensions = Default::default();
+            let s = NetHeaders::Ipv6(h.clone(), e.clone());
+            assert_eq!(s.ipv4_ref(), None);
+        }
+    }
+
+    #[test]
+    fn ipv6_ref() {
+        // 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);
+        }
+        // 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)));
+        }
+    }
+
+    #[test]
+    fn header_len() {
+        // ipv4
+        {
+            let h: Ipv4Header = Default::default();
+            let e: Ipv4Extensions = Default::default();
+            let s = NetHeaders::Ipv4(h.clone(), e.clone());
+            assert_eq!(s.header_len(), h.header_len() + e.header_len());
+        }
+        // ipv6
+        {
+            let h: Ipv6Header = Default::default();
+            let e: Ipv6Extensions = Default::default();
+            let s = NetHeaders::Ipv6(h.clone(), e.clone());
+            assert_eq!(s.header_len(), h.header_len() + e.header_len());
+        }
+    }
+
+    #[test]
+    fn from() {
+        // ipv4
+        {
+            let h: Ipv4Header = Default::default();
+            let e: Ipv4Extensions = Default::default();
+            let s = IpHeaders::Ipv4(h.clone(), e.clone());
+            let a: NetHeaders = s.clone().into();
+            assert_eq!(a, NetHeaders::Ipv4(h.clone(), e.clone()));
+        }
+        // ipv6
+        {
+            let h: Ipv6Header = Default::default();
+            let e: Ipv6Extensions = Default::default();
+            let s = IpHeaders::Ipv6(h.clone(), e.clone());
+            let a: NetHeaders = s.clone().into();
+            assert_eq!(a, NetHeaders::Ipv6(h.clone(), e.clone()));
+        }
+    }
+}
diff --git a/src/net/net_slice.rs b/src/net/net_slice.rs
new file mode 100644
index 0000000..3ab8542
--- /dev/null
+++ b/src/net/net_slice.rs
@@ -0,0 +1,205 @@
+use crate::*;
+
+/// Deprecated use [`crate::NetSlice`] or [`crate::IpSlice`] instead.
+#[cfg(feature = "std")]
+#[deprecated(
+    since = "0.14.0",
+    note = "Deprecated use crate::NetSlice or crate::IpSlice instead"
+)]
+pub use NetSlice as InternetSlice;
+
+/// Slice containing the network headers & payloads (e.g. IPv4, IPv6, ARP).
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum NetSlice<'a> {
+    /// The ipv4 header & the decoded extension headers.
+    Ipv4(Ipv4Slice<'a>),
+    /// The ipv6 header & the decoded extension headers.
+    Ipv6(Ipv6Slice<'a>),
+}
+
+impl<'a> NetSlice<'a> {
+    /// Returns a reference to ip payload if the net slice contains
+    /// an ipv4 or ipv6 slice.
+    #[inline]
+    pub fn ip_payload_ref(&self) -> Option<&IpPayloadSlice<'a>> {
+        match self {
+            NetSlice::Ipv4(s) => Some(&s.payload),
+            NetSlice::Ipv6(s) => Some(&s.payload),
+        }
+    }
+}
+
+impl<'a> From<IpSlice<'a>> for NetSlice<'a> {
+    #[inline]
+    fn from(value: IpSlice<'a>) -> NetSlice<'a> {
+        match value {
+            IpSlice::Ipv4(ipv4) => NetSlice::Ipv4(ipv4),
+            IpSlice::Ipv6(ipv6) => NetSlice::Ipv6(ipv6),
+        }
+    }
+}
+
+impl<'a> From<Ipv4Slice<'a>> for NetSlice<'a> {
+    #[inline]
+    fn from(value: Ipv4Slice<'a>) -> NetSlice<'a> {
+        NetSlice::Ipv4(value)
+    }
+}
+
+impl<'a> From<Ipv6Slice<'a>> for NetSlice<'a> {
+    #[inline]
+    fn from(value: Ipv6Slice<'a>) -> NetSlice<'a> {
+        NetSlice::Ipv6(value)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::*;
+    use alloc::{format, vec::Vec};
+
+    #[test]
+    fn debug() {
+        let bytes = Ipv6Header {
+            next_header: IpNumber::UDP,
+            ..Default::default()
+        }
+        .to_bytes();
+        let s = Ipv6Slice::from_slice(&bytes).unwrap();
+        let n = NetSlice::Ipv6(s.clone());
+        assert_eq!(format!("{n:?}"), format!("Ipv6({s:?})"));
+    }
+
+    #[test]
+    fn clone_eq() {
+        let bytes = Ipv6Header {
+            next_header: IpNumber::UDP,
+            ..Default::default()
+        }
+        .to_bytes();
+        let s = NetSlice::Ipv6(Ipv6Slice::from_slice(&bytes).unwrap());
+        assert_eq!(s, s.clone())
+    }
+
+    #[test]
+    fn ip_payload_ref() {
+        // ipv4
+        {
+            let payload = [1, 2, 3, 4];
+            let bytes = {
+                let mut bytes = Vec::with_capacity(Ipv4Header::MIN_LEN + 4);
+                bytes.extend_from_slice(
+                    &(Ipv4Header {
+                        total_len: Ipv4Header::MIN_LEN_U16 + 4,
+                        protocol: IpNumber::UDP,
+                        ..Default::default()
+                    })
+                    .to_bytes(),
+                );
+                bytes.extend_from_slice(&payload);
+                bytes
+            };
+            let s = NetSlice::Ipv4(Ipv4Slice::from_slice(&bytes).unwrap());
+            assert_eq!(
+                s.ip_payload_ref(),
+                Some(&IpPayloadSlice {
+                    ip_number: IpNumber::UDP,
+                    fragmented: false,
+                    len_source: LenSource::Ipv4HeaderTotalLen,
+                    payload: &payload
+                })
+            );
+        }
+        // ipv6
+        {
+            let payload = [1, 2, 3, 4];
+            let bytes = {
+                let mut bytes = Vec::with_capacity(Ipv6Header::LEN + 4);
+                bytes.extend_from_slice(
+                    &(Ipv6Header {
+                        next_header: IpNumber::UDP,
+                        payload_length: 4,
+                        ..Default::default()
+                    })
+                    .to_bytes(),
+                );
+                bytes.extend_from_slice(&payload);
+                bytes
+            };
+            let s = NetSlice::Ipv6(Ipv6Slice::from_slice(&bytes).unwrap());
+            assert_eq!(
+                s.ip_payload_ref(),
+                Some(&IpPayloadSlice {
+                    ip_number: IpNumber::UDP,
+                    fragmented: false,
+                    len_source: LenSource::Ipv6HeaderPayloadLen,
+                    payload: &payload
+                })
+            );
+        }
+    }
+
+    #[test]
+    fn from() {
+        // IpSlice::Ipv4
+        {
+            let payload = [1, 2, 3, 4];
+            let bytes = {
+                let mut bytes = Vec::with_capacity(Ipv4Header::MIN_LEN + 4);
+                bytes.extend_from_slice(
+                    &(Ipv4Header {
+                        total_len: Ipv4Header::MIN_LEN_U16 + 4,
+                        protocol: IpNumber::UDP,
+                        ..Default::default()
+                    })
+                    .to_bytes(),
+                );
+                bytes.extend_from_slice(&payload);
+                bytes
+            };
+            let i = Ipv4Slice::from_slice(&bytes).unwrap();
+            let actual: NetSlice = IpSlice::Ipv4(i.clone()).into();
+            assert_eq!(NetSlice::Ipv4(i.clone()), actual);
+        }
+        // Ipv4Slice
+        {
+            let payload = [1, 2, 3, 4];
+            let bytes = {
+                let mut bytes = Vec::with_capacity(Ipv4Header::MIN_LEN + 4);
+                bytes.extend_from_slice(
+                    &(Ipv4Header {
+                        total_len: Ipv4Header::MIN_LEN_U16 + 4,
+                        protocol: IpNumber::UDP,
+                        ..Default::default()
+                    })
+                    .to_bytes(),
+                );
+                bytes.extend_from_slice(&payload);
+                bytes
+            };
+            let i = Ipv4Slice::from_slice(&bytes).unwrap();
+            let actual: NetSlice = i.clone().into();
+            assert_eq!(NetSlice::Ipv4(i.clone()), actual);
+        }
+        // IpSlice::Ipv6
+        {
+            let payload = [1, 2, 3, 4];
+            let bytes = {
+                let mut bytes = Vec::with_capacity(Ipv6Header::LEN + 4);
+                bytes.extend_from_slice(
+                    &(Ipv6Header {
+                        next_header: IpNumber::UDP,
+                        payload_length: 4,
+                        ..Default::default()
+                    })
+                    .to_bytes(),
+                );
+                bytes.extend_from_slice(&payload);
+                bytes
+            };
+            let i = Ipv6Slice::from_slice(&bytes).unwrap();
+            let actual: NetSlice = i.clone().into();
+            assert_eq!(NetSlice::Ipv6(i.clone()), actual);
+        }
+    }
+}
diff --git a/src/packet_builder.rs b/src/packet_builder.rs
new file mode 100644
index 0000000..ee92031
--- /dev/null
+++ b/src/packet_builder.rs
@@ -0,0 +1,3858 @@
+use crate::err::packet::BuildWriteError;
+
+use super::*;
+
+use std::{io, marker};
+
+/// Helper for building packets.
+///
+/// The packet builder allows the easy construction of a packet from the
+/// ethernet II layer downwards including ipv6, ipv4, the udp header and the
+/// actual payload. The packet builder automatically calculates lengths & checksums
+/// for ip & udp and set type identifiers for ethernetII and ip. This makes it
+/// easy and less error prone to construct custom packets.
+///
+/// # Example:
+///
+/// Generating a packet that starts with an Ethernet II header:
+///
+/// ```
+/// use etherparse::PacketBuilder;
+///
+/// let builder = PacketBuilder::
+///     ethernet2([1,2,3,4,5,6],     //source mac
+///               [7,8,9,10,11,12]) //destination mac
+///    .ipv4([192,168,1,1], //source ip
+///          [192,168,1,2], //destination ip
+///          20)            //time to life
+///    .udp(21,    //source port
+///         1234); //destination port
+///
+/// //payload of the udp packet
+/// let payload = [1,2,3,4,5,6,7,8];
+///
+/// //get some memory to store the result
+/// let mut result = Vec::<u8>::with_capacity(
+///                     builder.size(payload.len()));
+///
+/// //serialize
+/// builder.write(&mut result, &payload).unwrap();
+/// println!("{:?}", result);
+/// ```
+///
+/// # Options
+///
+/// * Starting Options:
+///     * [`PacketBuilder::ethernet2`]
+///     * [`PacketBuilder::linux_sll`]
+///     * [`PacketBuilder::ip`]
+///     * [`PacketBuilder::ipv4`]
+///     * [`PacketBuilder::ipv6`]
+/// * Options after an Ethernet2 header was added:
+///     * [`PacketBuilderStep<Ethernet2Header>::vlan`]
+///     * [`PacketBuilderStep<Ethernet2Header>::single_vlan`]
+///     * [`PacketBuilderStep<Ethernet2Header>::double_vlan`]
+///     * [`PacketBuilderStep<Ethernet2Header>::ip`]
+///     * [`PacketBuilderStep<Ethernet2Header>::ipv4`]
+///     * [`PacketBuilderStep<Ethernet2Header>::ipv6`]
+/// * Options after a Linux Cooked Capture v1 (SLL) was added:
+///     * [`PacketBuilderStep<LinuxSllHeader>::ip`]
+///     * [`PacketBuilderStep<LinuxSllHeader>::ipv4`]
+///     * [`PacketBuilderStep<LinuxSllHeader>::ipv6`]
+/// * Options after an Vlan header was added:
+///     * [`PacketBuilderStep<VlanHeader>::ip`]
+///     * [`PacketBuilderStep<VlanHeader>::ipv4`]
+///     * [`PacketBuilderStep<VlanHeader>::ipv6`]
+/// * Options after an IP header was added:
+///     * [`PacketBuilderStep<IpHeaders>::write`]
+///     * [`PacketBuilderStep<IpHeaders>::tcp`]
+///     * [`PacketBuilderStep<IpHeaders>::udp`]
+///     * [`PacketBuilderStep<IpHeaders>::icmpv4`]
+///     * [`PacketBuilderStep<IpHeaders>::icmpv4_raw`]
+///     * [`PacketBuilderStep<IpHeaders>::icmpv4_echo_request`]
+///     * [`PacketBuilderStep<IpHeaders>::icmpv4_echo_reply`]
+///     * [`PacketBuilderStep<IpHeaders>::icmpv6`]
+///     * [`PacketBuilderStep<IpHeaders>::icmpv6_raw`]
+///     * [`PacketBuilderStep<IpHeaders>::icmpv6_echo_request`]
+///     * [`PacketBuilderStep<IpHeaders>::icmpv6_echo_reply`]
+/// * Options after an TCP header was added:
+///     * [`PacketBuilderStep<TcpHeader>::write`]
+///     * [`PacketBuilderStep<TcpHeader>::size`]
+///     * [`PacketBuilderStep<TcpHeader>::ns`]
+///     * [`PacketBuilderStep<TcpHeader>::fin`]
+///     * [`PacketBuilderStep<TcpHeader>::syn`]
+///     * [`PacketBuilderStep<TcpHeader>::rst`]
+///     * [`PacketBuilderStep<TcpHeader>::psh`]
+///     * [`PacketBuilderStep<TcpHeader>::ack`]
+///     * [`PacketBuilderStep<TcpHeader>::urg`]
+///     * [`PacketBuilderStep<TcpHeader>::ece`]
+///     * [`PacketBuilderStep<TcpHeader>::cwr`]
+///     * [`PacketBuilderStep<TcpHeader>::options`]
+///     * [`PacketBuilderStep<TcpHeader>::options_raw`]
+/// * Options after an UDP header was added:
+///     * [`PacketBuilderStep<UdpHeader>::write`]
+///     * [`PacketBuilderStep<UdpHeader>::size`]
+/// * Options after an ICMPv4 header was added:
+///     * [`PacketBuilderStep<Icmpv4Header>::write`]
+///     * [`PacketBuilderStep<Icmpv4Header>::size`]
+/// * Options after an ICMPv6 header was added:
+///     * [`PacketBuilderStep<Icmpv6Header>::write`]
+///     * [`PacketBuilderStep<Icmpv6Header>::size`]
+///
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub struct PacketBuilder {}
+
+impl PacketBuilder {
+    /// Start an packet with an ethernetII header.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::PacketBuilder;
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ethernet2([1,2,3,4,5,6],     //source mac
+    ///               [7,8,9,10,11,12]) //destination mac
+    ///    .ipv4([192,168,1,1], //source ip
+    ///          [192,168,1,2], //destination ip
+    ///          20)            //time to life
+    ///    .udp(21,    //source port
+    ///         1234); //destination port
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn ethernet2(source: [u8; 6], destination: [u8; 6]) -> PacketBuilderStep<Ethernet2Header> {
+        PacketBuilderStep {
+            state: PacketImpl {
+                link_header: Some(LinkHeader::Ethernet2(Ethernet2Header {
+                    source,
+                    destination,
+                    ether_type: EtherType(0), //the type identifier
+                })),
+                vlan_header: None,
+                ip_header: None,
+                transport_header: None,
+            },
+            _marker: marker::PhantomData::<Ethernet2Header> {},
+        }
+    }
+
+    /// Start an packet with an Linux Cooked Catpure (v1) header.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::{PacketBuilder, LinuxSllPacketType};
+    /// #
+    /// let builder = PacketBuilder::
+    ///     linux_sll(LinuxSllPacketType::OTHERHOST, //packet type
+    ///               6, //sender address valid length
+    ///               [1,2,3,4,5,6,0,0]) //sender address with padding
+    ///    .ipv4([192,168,1,1], //source ip
+    ///          [192,168,1,2], //destination ip
+    ///          20)            //time to life
+    ///    .udp(21,    //source port
+    ///         1234); //destination port
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn linux_sll(
+        packet_type: LinuxSllPacketType,
+        sender_address_valid_length: u16,
+        sender_address: [u8; 8],
+    ) -> PacketBuilderStep<LinuxSllHeader> {
+        PacketBuilderStep {
+            state: PacketImpl {
+                link_header: Some(LinkHeader::LinuxSll(LinuxSllHeader {
+                    packet_type,
+                    arp_hrd_type: ArpHardwareId::ETHER,
+                    sender_address_valid_length,
+                    sender_address,
+                    protocol_type: LinuxSllProtocolType::EtherType(EtherType(0)), // Will be overwitten when writing depending on the net layer
+                })),
+                vlan_header: None,
+                ip_header: None,
+                transport_header: None,
+            },
+            _marker: marker::PhantomData::<LinuxSllHeader> {},
+        }
+    }
+
+    /// Starts a packet with an IPv4 header.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::PacketBuilder;
+    /// #
+    /// let builder = PacketBuilder::
+    ///    ipv4([192,168,1,1],  //source ip
+    ///          [192,168,1,2], //destination ip
+    ///          20)            //time to life
+    ///    .udp(21,    //source port
+    ///         1234); //destination port
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///     
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn ipv4(
+        source: [u8; 4],
+        destination: [u8; 4],
+        time_to_live: u8,
+    ) -> PacketBuilderStep<IpHeaders> {
+        PacketBuilderStep {
+            state: PacketImpl {
+                link_header: None,
+                vlan_header: None,
+                ip_header: None,
+                transport_header: None,
+            },
+            _marker: marker::PhantomData::<Ethernet2Header> {},
+        }
+        .ipv4(source, destination, time_to_live)
+    }
+
+    /// Start a packet with an IPv6 header.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::PacketBuilder;
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ipv6(
+    ///         //source
+    ///         [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26],
+    ///         //destination
+    ///         [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],
+    ///         //hop_limit
+    ///         47)
+    ///    .udp(21,    //source port
+    ///         1234); //destination port
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn ipv6(
+        source: [u8; 16],
+        destination: [u8; 16],
+        hop_limit: u8,
+    ) -> PacketBuilderStep<IpHeaders> {
+        PacketBuilderStep {
+            state: PacketImpl {
+                link_header: None,
+                vlan_header: None,
+                ip_header: None,
+                transport_header: None,
+            },
+            _marker: marker::PhantomData::<Ethernet2Header> {},
+        }
+        .ipv6(source, destination, hop_limit)
+    }
+
+    /// Starts a packet with an arbitrary IP header (length, protocol/next_header & checksum fields will be overwritten based on the rest of the packet).
+    ///
+    /// # Examples
+    ///
+    /// With an IPv4 header:
+    ///
+    /// ```
+    /// # use etherparse::*;
+    /// #
+    /// let builder = PacketBuilder::
+    ///    //payload_len, protocol & checksum will be replaced during write
+    ///    ip(IpHeaders::Ipv4(
+    ///        Ipv4Header::new(
+    ///            0, //payload_len will be replaced during write
+    ///            12, //time_to_live
+    ///            ip_number::UDP, //will be replaced during write
+    ///            [0,1,2,3], //source
+    ///            [4,5,6,7] //destination
+    ///        ).unwrap(),
+    ///        Default::default()))
+    ///    .udp(21,    //source port
+    ///         1234); //destination port
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    ///
+    /// With an IPv6 header:
+    ///
+    /// ```
+    /// # use etherparse::*;
+    /// #
+    /// let builder = PacketBuilder::
+    ///    ip(IpHeaders::Ipv6(
+    ///         Ipv6Header{
+    ///             traffic_class: 0,
+    ///             flow_label: 0.try_into().unwrap(),
+    ///             hop_limit: 4.try_into().unwrap(),
+    ///             source: [0;16],
+    ///             destination: [0;16],
+    ///             // payload_length & next_header will be replaced during write
+    ///             ..Default::default()
+    ///         },
+    ///         Default::default()))
+    ///    .udp(21,    //source port
+    ///         1234); //destination port
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///     
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn ip(ip_header: IpHeaders) -> PacketBuilderStep<IpHeaders> {
+        PacketBuilderStep {
+            state: PacketImpl {
+                link_header: None,
+                vlan_header: None,
+                ip_header: None,
+                transport_header: None,
+            },
+            _marker: marker::PhantomData::<Ethernet2Header> {},
+        }
+        .ip(ip_header)
+    }
+}
+
+struct PacketImpl {
+    link_header: Option<LinkHeader>,
+    ip_header: Option<IpHeaders>,
+    vlan_header: Option<VlanHeader>,
+    transport_header: Option<TransportHeader>,
+}
+
+///An unfinished packet that is build with the packet builder
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+pub struct PacketBuilderStep<LastStep> {
+    state: PacketImpl,
+    _marker: marker::PhantomData<LastStep>,
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl PacketBuilderStep<Ethernet2Header> {
+    /// Add an IPv4 header
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::PacketBuilder;
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ethernet2([1,2,3,4,5,6],     //source mac
+    ///               [7,8,9,10,11,12]) //destination mac
+    ///    .ipv4([192,168,1,1], //source ip
+    ///          [192,168,1,2], //destination ip
+    ///          20)            //time to life
+    ///    .udp(21,    //source port
+    ///         1234); //destination port
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn ipv4(
+        mut self,
+        source: [u8; 4],
+        destination: [u8; 4],
+        time_to_live: u8,
+    ) -> PacketBuilderStep<IpHeaders> {
+        //add ip header
+        self.state.ip_header = Some(IpHeaders::Ipv4(
+            Ipv4Header {
+                source,
+                destination,
+                time_to_live,
+                ..Default::default()
+            },
+            Default::default(),
+        ));
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<IpHeaders> {},
+        }
+    }
+
+    /// Add an IP header (length, protocol/next_header & checksum fields will be overwritten based on the rest of the packet).
+    ///
+    /// # Examples
+    ///
+    /// With an IPv4 header:
+    ///
+    /// ```
+    /// # use etherparse::*;
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ethernet2([1,2,3,4,5,6],
+    ///               [7,8,9,10,11,12])
+    ///    //payload_len, protocol & checksum will be replaced during write
+    ///    .ip(IpHeaders::Ipv4(
+    ///        Ipv4Header::new(
+    ///            0, //payload_len will be replaced during write
+    ///            12, //time_to_live
+    ///            ip_number::UDP, //will be replaced during write
+    ///            [0,1,2,3], //source
+    ///            [4,5,6,7] //destination
+    ///        ).unwrap(),
+    ///        Default::default()));
+    /// ```
+    ///
+    /// With an IPv6 header:
+    ///
+    /// ```
+    /// # use etherparse::*;
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ethernet2([1,2,3,4,5,6],
+    ///               [7,8,9,10,11,12])
+    ///    .ip(IpHeaders::Ipv6(
+    ///         Ipv6Header{
+    ///             traffic_class: 0,
+    ///             flow_label: 0.try_into().unwrap(),
+    ///             hop_limit: 4,
+    ///             source: [0;16],
+    ///             destination: [0;16],
+    ///             // payload_length & next_header will be replaced during write
+    ///             ..Default::default()
+    ///         },
+    ///         Default::default()));
+    /// ```
+    pub fn ip(mut self, ip_header: IpHeaders) -> PacketBuilderStep<IpHeaders> {
+        //add ip header
+        self.state.ip_header = Some(ip_header);
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<IpHeaders> {},
+        }
+    }
+
+    /// Add an IPv6 header
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::PacketBuilder;
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ethernet2([1,2,3,4,5,6],
+    ///               [7,8,9,10,11,12])
+    ///     .ipv6(
+    ///         //source
+    ///         [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26],
+    ///         //destination
+    ///         [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],
+    ///         //hop_limit
+    ///         47)
+    ///    .udp(21,    //source port
+    ///         1234); //destination port
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn ipv6(
+        mut self,
+        source: [u8; 16],
+        destination: [u8; 16],
+        hop_limit: u8,
+    ) -> PacketBuilderStep<IpHeaders> {
+        self.state.ip_header = Some(IpHeaders::Ipv6(
+            Ipv6Header {
+                traffic_class: 0,
+                flow_label: Ipv6FlowLabel::ZERO,
+                payload_length: 0,          //filled in on write
+                next_header: IpNumber(255), //filled in on write
+                hop_limit,
+                source,
+                destination,
+            },
+            Default::default(),
+        ));
+
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<IpHeaders> {},
+        }
+    }
+
+    /// Adds a vlan tagging header with the given vlan identifier
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::{PacketBuilder, SingleVlanHeader, VlanHeader};
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ethernet2([1,2,3,4,5,6],     //source mac
+    ///               [7,8,9,10,11,12]) //destination mac
+    ///     .vlan(VlanHeader::Single(
+    ///         SingleVlanHeader{
+    ///             pcp: 1.try_into().unwrap(),
+    ///             drop_eligible_indicator: false,
+    ///             vlan_id: 0x123.try_into().unwrap(),
+    ///             ether_type: 0.into() // will be overwritten during write
+    ///         }))
+    ///     .ipv4([192,168,1,1], //source ip
+    ///           [192,168,1,2], //destination ip
+    ///           20)            //time to life
+    ///     .udp(21,    //source port
+    ///          1234); //destination port
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn vlan(mut self, vlan: VlanHeader) -> PacketBuilderStep<VlanHeader> {
+        self.state.vlan_header = Some(vlan);
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<VlanHeader> {},
+        }
+    }
+
+    /// Adds a vlan tagging header with the given vlan identifier
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::{PacketBuilder, SingleVlanHeader, VlanHeader};
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ethernet2([1,2,3,4,5,6],     //source mac
+    ///               [7,8,9,10,11,12]) //destination mac
+    ///     .single_vlan(0x123.try_into().unwrap()) // vlan identifier
+    ///     .ipv4([192,168,1,1], //source ip
+    ///           [192,168,1,2], //destination ip
+    ///           20)            //time to life
+    ///     .udp(21,    //source port
+    ///          1234); //destination port
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn single_vlan(mut self, vlan_identifier: VlanId) -> PacketBuilderStep<VlanHeader> {
+        self.state.vlan_header = Some(VlanHeader::Single(SingleVlanHeader {
+            pcp: VlanPcp::ZERO,
+            drop_eligible_indicator: false,
+            vlan_id: vlan_identifier,
+            ether_type: EtherType(0), //will be set automatically during write
+        }));
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<VlanHeader> {},
+        }
+    }
+
+    /// Adds two vlan tagging header with the given vlan identifiers (also known as double vlan tagging).
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::{PacketBuilder, SingleVlanHeader, VlanHeader};
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ethernet2([1,2,3,4,5,6],     //source mac
+    ///               [7,8,9,10,11,12]) //destination mac
+    ///     .double_vlan(0x123.try_into().unwrap(), // outer vlan identifier
+    ///                  0x234.try_into().unwrap()) // inner vlan identifier
+    ///     .ipv4([192,168,1,1], //source ip
+    ///           [192,168,1,2], //destination ip
+    ///           20)            //time to life
+    ///     .udp(21,    //source port
+    ///          1234); //destination port
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn double_vlan(
+        mut self,
+        outer_vlan_identifier: VlanId,
+        inner_vlan_identifier: VlanId,
+    ) -> PacketBuilderStep<VlanHeader> {
+        self.state.vlan_header = Some(VlanHeader::Double(DoubleVlanHeader {
+            outer: SingleVlanHeader {
+                pcp: VlanPcp::ZERO,
+                drop_eligible_indicator: false,
+                vlan_id: outer_vlan_identifier,
+                ether_type: EtherType(0), //will be set automatically during write
+            },
+            inner: SingleVlanHeader {
+                pcp: VlanPcp::ZERO,
+                drop_eligible_indicator: false,
+                vlan_id: inner_vlan_identifier,
+                ether_type: EtherType(0), //will be set automatically during write
+            },
+        }));
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<VlanHeader> {},
+        }
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl PacketBuilderStep<LinuxSllHeader> {
+    /// Add an ip header (length, protocol/next_header & checksum fields will be overwritten based on the rest of the packet).
+    ///
+    /// # Example IPv4
+    /// ```
+    /// # use etherparse::*;
+    /// #
+    /// let builder = PacketBuilder::
+    ///     linux_sll(LinuxSllPacketType::OTHERHOST, //packet type
+    ///               6, //sender address valid length
+    ///               [1,2,3,4,5,6,0,0]) //sender address with padding
+    ///    //payload_len, protocol & checksum will be replaced during write
+    ///    .ip(IpHeaders::Ipv4(
+    ///         Ipv4Header::new(
+    ///             0, //payload_len will be replaced during write
+    ///             12, //time_to_live
+    ///             ip_number::UDP, //will be replaced during write
+    ///             [0,1,2,3], //source
+    ///             [4,5,6,7] //destination
+    ///         ).unwrap(),
+    ///         Default::default() // IPv4 extension headers (default is none)
+    ///     ));
+    /// ```
+    ///
+    /// # Example IPv6
+    /// ```
+    /// # use etherparse::*;
+    /// #
+    /// let builder = PacketBuilder::
+    ///     linux_sll(LinuxSllPacketType::OTHERHOST, //packet type
+    ///               6, //sender address valid length
+    ///               [1,2,3,4,5,6,0,0]) //sender address with padding
+    ///    .ip(IpHeaders::Ipv6(
+    ///         Ipv6Header{
+    ///             traffic_class: 0,
+    ///             flow_label: 0.try_into().unwrap(),
+    ///             hop_limit: 4,
+    ///             source: [0;16],
+    ///             destination: [0;16],
+    ///             // payload_length & next_header will be replaced during write
+    ///             ..Default::default()
+    ///         },
+    ///         Default::default() // IPv6 extension headers (default is none)
+    ///     ));
+    /// ```
+    pub fn ip(self, ip_header: IpHeaders) -> PacketBuilderStep<IpHeaders> {
+        //use the method from the Ethernet2Header implementation
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<Ethernet2Header> {},
+        }
+        .ip(ip_header)
+    }
+
+    /// Add an IPv6 header
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::{PacketBuilder, LinuxSllPacketType, ArpHardwareId, LinuxSllProtocolType, EtherType};
+    /// #
+    /// let builder = PacketBuilder::
+    ///     linux_sll(LinuxSllPacketType::OTHERHOST, //packet type
+    ///               6, //sender address valid length
+    ///               [1,2,3,4,5,6,0,0]) //sender address with padding
+    ///     .ipv6(
+    ///         //source
+    ///         [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26],
+    ///         //destination
+    ///         [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],
+    ///         //hop_limit
+    ///         47)
+    ///     .udp(21,    //source port
+    ///          1234); //destination port
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn ipv6(
+        self,
+        source: [u8; 16],
+        destination: [u8; 16],
+        hop_limit: u8,
+    ) -> PacketBuilderStep<IpHeaders> {
+        //use the method from the Ethernet2Header implementation
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<Ethernet2Header> {},
+        }
+        .ipv6(source, destination, hop_limit)
+    }
+
+    /// Add an IPv4 header
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::{PacketBuilder, LinuxSllPacketType, ArpHardwareId, LinuxSllProtocolType, EtherType};
+    /// #
+    /// let builder = PacketBuilder::
+    ///     linux_sll(LinuxSllPacketType::OTHERHOST, //packet type
+    ///               6, //sender address valid length
+    ///               [1,2,3,4,5,6,0,0]) //sender address with padding
+    ///     .ipv4([192,168,1,1], //source ip
+    ///           [192,168,1,2], //destination ip
+    ///           20)            //time to life
+    ///     .udp(21,    //source port
+    ///          1234); //destination port
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn ipv4(
+        self,
+        source: [u8; 4],
+        destination: [u8; 4],
+        time_to_live: u8,
+    ) -> PacketBuilderStep<IpHeaders> {
+        //use the method from the Ethernet2Header implementation
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<Ethernet2Header> {},
+        }
+        .ipv4(source, destination, time_to_live)
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl PacketBuilderStep<VlanHeader> {
+    ///Add an ip header (length, protocol/next_header & checksum fields will be overwritten based on the rest of the packet).
+    ///
+    /// # Example IPv4
+    /// ```
+    /// # use etherparse::*;
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ethernet2([1,2,3,4,5,6],
+    ///               [7,8,9,10,11,12])
+    ///    .single_vlan(0x132.try_into().unwrap())
+    ///    //payload_len, protocol & checksum will be replaced during write
+    ///    .ip(IpHeaders::Ipv4(
+    ///         Ipv4Header::new(
+    ///             0, //payload_len will be replaced during write
+    ///             12, //time_to_live
+    ///             ip_number::UDP, //will be replaced during write
+    ///             [0,1,2,3], //source
+    ///             [4,5,6,7] //destination
+    ///         ).unwrap(),
+    ///         Default::default() // IPv4 extension headers (default is none)
+    ///     ));
+    /// ```
+    ///
+    /// # Example IPv6
+    /// ```
+    /// # use etherparse::*;
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ethernet2([1,2,3,4,5,6],
+    ///               [7,8,9,10,11,12])
+    ///    .single_vlan(0x132.try_into().unwrap())
+    ///    .ip(IpHeaders::Ipv6(
+    ///         Ipv6Header{
+    ///             traffic_class: 0,
+    ///             flow_label: 0.try_into().unwrap(),
+    ///             hop_limit: 4,
+    ///             source: [0;16],
+    ///             destination: [0;16],
+    ///             // payload_length & next_header will be replaced during write
+    ///             ..Default::default()
+    ///         },
+    ///         Default::default() // IPv6 extension headers (default is none)
+    ///     ));
+    /// ```
+    pub fn ip(self, ip_header: IpHeaders) -> PacketBuilderStep<IpHeaders> {
+        //use the method from the Ethernet2Header implementation
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<Ethernet2Header> {},
+        }
+        .ip(ip_header)
+    }
+
+    /// Add an IPv6 header
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::{PacketBuilder, SingleVlanHeader, VlanHeader};
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ethernet2([1,2,3,4,5,6],     //source mac
+    ///               [7,8,9,10,11,12]) //destination mac
+    ///     .single_vlan(0x123.try_into().unwrap()) // vlan identifier
+    ///     .ipv6(
+    ///         //source
+    ///         [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26],
+    ///         //destination
+    ///         [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],
+    ///         //hop_limit
+    ///         47)
+    ///     .udp(21,    //source port
+    ///          1234); //destination port
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn ipv6(
+        self,
+        source: [u8; 16],
+        destination: [u8; 16],
+        hop_limit: u8,
+    ) -> PacketBuilderStep<IpHeaders> {
+        //use the method from the Ethernet2Header implementation
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<Ethernet2Header> {},
+        }
+        .ipv6(source, destination, hop_limit)
+    }
+
+    /// Add an IPv4 header
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::{PacketBuilder, SingleVlanHeader, VlanHeader};
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ethernet2([1,2,3,4,5,6],     //source mac
+    ///               [7,8,9,10,11,12]) //destination mac
+    ///     .single_vlan(0x123.try_into().unwrap()) // vlan identifier
+    ///     .ipv4([192,168,1,1], //source ip
+    ///           [192,168,1,2], //destination ip
+    ///           20)            //time to life
+    ///     .udp(21,    //source port
+    ///          1234); //destination port
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn ipv4(
+        self,
+        source: [u8; 4],
+        destination: [u8; 4],
+        time_to_live: u8,
+    ) -> PacketBuilderStep<IpHeaders> {
+        //use the method from the Ethernet2Header implementation
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<Ethernet2Header> {},
+        }
+        .ipv4(source, destination, time_to_live)
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl PacketBuilderStep<IpHeaders> {
+    /// Adds an ICMPv4 header of the given [`Icmpv4Type`] to the packet.
+    ///
+    /// If an ICMPv4 header gets added the payload used during the builders `write`
+    /// call contains the bytes after the header and has different meanings
+    /// and contents based on the type. Usually all statically sized values
+    /// known based on the ICMPv4 type & code are part of the header and the
+    /// payload is used to store contains the dynamic parts of the ICMPv4 packet.
+    ///
+    /// Check [`Icmpv4Type`] for a documentation which values are part of the
+    /// header and what is stored as part of the payload.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::{PacketBuilder, Icmpv4Type, icmpv4};
+    /// #
+    /// let builder = PacketBuilder::
+    ///    ipv4([192,168,1,1],  //source ip
+    ///          [192,168,1,2], //destination ip
+    ///          20)            //time to life
+    ///    .icmpv4(
+    ///         Icmpv4Type::TimeExceeded(
+    ///             icmpv4::TimeExceededCode::TtlExceededInTransit
+    ///         )
+    ///     );
+    ///
+    /// // what is part of the payload depends on the Icmpv4Type
+    /// //
+    /// // In case of `Icmpv4Type::TimeExceeded` the "internet header
+    /// // + 64 bits of the original data datagram" should be given as
+    /// // the payload
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn icmpv4(mut self, icmp_type: Icmpv4Type) -> PacketBuilderStep<Icmpv4Header> {
+        self.state.transport_header = Some(TransportHeader::Icmpv4(Icmpv4Header {
+            icmp_type,
+            checksum: 0, // calculated later
+        }));
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<Icmpv4Header> {},
+        }
+    }
+
+    /// Adds an ICMPv4 header based on raw numbers.
+    ///
+    /// This can be useful when trying to build an ICMPv4 packet
+    /// which is not fully supported by etherparse and is the equivalent
+    /// of using [`Icmpv4Type::Unknown`] together with
+    /// [`PacketBuilderStep<IpHeaders>::icmpv4`].
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::PacketBuilder;
+    /// #
+    /// let builder = PacketBuilder::
+    ///    ipv4([192,168,1,1],  //source ip
+    ///          [192,168,1,2], //destination ip
+    ///          20)            //time to life
+    ///    .icmpv4_raw(
+    ///         253, // ICMPv4 type (e.g. 253 is RFC3692-style Experiment 1)
+    ///         0, // ICMPv4 code
+    ///         [1,2,3,4]  // bytes 5-8 in the ICMPv4 header
+    ///     );
+    ///
+    /// // the payload is written after the 8 byte raw ICMPv4 header
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// // get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// // serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn icmpv4_raw(
+        mut self,
+        type_u8: u8,
+        code_u8: u8,
+        bytes5to8: [u8; 4],
+    ) -> PacketBuilderStep<Icmpv4Header> {
+        let icmp_type = Icmpv4Type::Unknown {
+            type_u8,
+            code_u8,
+            bytes5to8,
+        };
+        self.state.transport_header = Some(TransportHeader::Icmpv4(Icmpv4Header {
+            icmp_type,
+            checksum: 0, // calculated later
+        }));
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<Icmpv4Header> {},
+        }
+    }
+
+    /// Adds an ICMPv4 echo request packet.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::PacketBuilder;
+    /// #
+    /// let builder = PacketBuilder::
+    ///    ipv4([192,168,1,1],  //source ip
+    ///          [192,168,1,2], //destination ip
+    ///          20)            //time to life
+    ///    .icmpv4_echo_request(
+    ///         123, // identifier
+    ///         456, // sequence number
+    ///     );
+    ///
+    /// // payload of the echo request
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// // get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// // serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn icmpv4_echo_request(mut self, id: u16, seq: u16) -> PacketBuilderStep<Icmpv4Header> {
+        let echo_header = IcmpEchoHeader { id, seq };
+        let icmpv4_echo = Icmpv4Header::new(Icmpv4Type::EchoRequest(echo_header));
+        self.state.transport_header = Some(TransportHeader::Icmpv4(icmpv4_echo));
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<Icmpv4Header> {},
+        }
+    }
+
+    /// Adds an ICMPv4 echo reply packet.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::PacketBuilder;
+    /// #
+    /// let builder = PacketBuilder::
+    ///    ipv4([192,168,1,1],  //source ip
+    ///          [192,168,1,2], //destination ip
+    ///          20)            //time to life
+    ///    .icmpv4_echo_reply(
+    ///         123, // identifier
+    ///         456, // sequence number
+    ///     );
+    ///
+    /// // payload of the echo reply
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// // get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// // serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn icmpv4_echo_reply(mut self, id: u16, seq: u16) -> PacketBuilderStep<Icmpv4Header> {
+        let echo_header = IcmpEchoHeader { id, seq };
+        let icmpv4_echo = Icmpv4Header::new(Icmpv4Type::EchoReply(echo_header));
+        self.state.transport_header = Some(TransportHeader::Icmpv4(icmpv4_echo));
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<Icmpv4Header> {},
+        }
+    }
+
+    /// Adds an ICMPv6 header of the given [`Icmpv6Type`] to the packet.
+    ///
+    /// If an ICMPv6 header gets added the payload used during the builders `write`
+    /// call contains the bytes after the header and has different meanings
+    /// and contents based on the type. Usually all statically sized values
+    /// known based on the ICMPv6 type & code are part of the header and the
+    /// payload is used to store contains the dynamic parts of the ICMPv6 packet.
+    ///
+    /// Check [`Icmpv6Type`] for a documentation which values are part of the
+    /// header and what is stored as part of the payload.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::{PacketBuilder, Icmpv6Type, icmpv6};
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ipv6(
+    ///         //source
+    ///         [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26],
+    ///         //destination
+    ///         [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],
+    ///         //hop_limit
+    ///         47)
+    ///    .icmpv6(
+    ///         Icmpv6Type::TimeExceeded(
+    ///             icmpv6::TimeExceededCode::HopLimitExceeded
+    ///         )
+    ///     );
+    ///
+    /// // what is part of the payload depends on the Icmpv6Type
+    /// //
+    /// // In case of `Icmpv6Type::TimeExceeded` "As much of invoking packet
+    /// // as possible without the ICMPv6 packet exceeding the minimum IPv6 MTU"
+    /// // should be given as the payload.
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn icmpv6(mut self, icmp_type: Icmpv6Type) -> PacketBuilderStep<Icmpv6Header> {
+        self.state.transport_header = Some(TransportHeader::Icmpv6(Icmpv6Header {
+            icmp_type,
+            checksum: 0, // calculated later
+        }));
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<Icmpv6Header> {},
+        }
+    }
+
+    /// Adds an ICMPv6 header based on raw values.
+    ///
+    /// This can be useful when trying to build an ICMPv6 packet
+    /// which is not fully supported by etherparse and is the equivalent
+    /// of using [`Icmpv6Type::Unknown`] together with
+    /// [`PacketBuilderStep<IpHeaders>::icmpv6`].
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::PacketBuilder;
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ipv6(
+    ///         //source
+    ///         [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26],
+    ///         //destination
+    ///         [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],
+    ///         //hop_limit
+    ///         47)
+    ///    .icmpv4_raw(
+    ///         200, // ICMPv6 type (e.g. 200 is for "private experimentation")
+    ///         0, // ICMPv6 code
+    ///         [1,2,3,4]  // bytes 5-8 in the ICMPv6 header
+    ///     );
+    ///
+    /// // the payload is written after the 8 byte raw ICMPv6 header
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn icmpv6_raw(
+        mut self,
+        type_u8: u8,
+        code_u8: u8,
+        bytes5to8: [u8; 4],
+    ) -> PacketBuilderStep<Icmpv6Header> {
+        let icmp_type = Icmpv6Type::Unknown {
+            type_u8,
+            code_u8,
+            bytes5to8,
+        };
+        self.state.transport_header = Some(TransportHeader::Icmpv6(Icmpv6Header {
+            icmp_type,
+            checksum: 0, // calculated later
+        }));
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<Icmpv6Header> {},
+        }
+    }
+
+    /// Adds an ICMPv6 echo reply packet.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::PacketBuilder;
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ipv6(
+    ///         //source
+    ///         [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26],
+    ///         //destination
+    ///         [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],
+    ///         //hop_limit
+    ///         47)
+    ///    .icmpv6_echo_request(
+    ///         123, // identifier
+    ///         456, // sequence number
+    ///     );
+    ///
+    /// // payload of the echo request
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn icmpv6_echo_request(mut self, id: u16, seq: u16) -> PacketBuilderStep<Icmpv6Header> {
+        let echo_header = IcmpEchoHeader { id, seq };
+        let icmpv6_echo = Icmpv6Header::new(Icmpv6Type::EchoRequest(echo_header));
+        self.state.transport_header = Some(TransportHeader::Icmpv6(icmpv6_echo));
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<Icmpv6Header> {},
+        }
+    }
+
+    /// Adds an ICMPv6 echo request packet.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::PacketBuilder;
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ipv6(
+    ///         //source
+    ///         [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26],
+    ///         //destination
+    ///         [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],
+    ///         //hop_limit
+    ///         47)
+    ///    .icmpv6_echo_reply(
+    ///         123, // identifier
+    ///         456, // sequence number
+    ///     );
+    ///
+    /// // payload of the echo reply
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn icmpv6_echo_reply(mut self, id: u16, seq: u16) -> PacketBuilderStep<Icmpv6Header> {
+        let echo_header = IcmpEchoHeader { seq, id };
+        let icmpv6_echo = Icmpv6Header::new(Icmpv6Type::EchoReply(echo_header));
+        self.state.transport_header = Some(TransportHeader::Icmpv6(icmpv6_echo));
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<Icmpv6Header> {},
+        }
+    }
+
+    /// Adds an UDP header.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::PacketBuilder;
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ethernet2([1,2,3,4,5,6],     //source mac
+    ///               [7,8,9,10,11,12]) //destination mac
+    ///    .ipv4([192,168,1,1], //source ip
+    ///          [192,168,1,2], //destination ip
+    ///          20)            //time to life
+    ///    .udp(21,    //source port
+    ///         1234); //destination port
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn udp(mut self, source_port: u16, destination_port: u16) -> PacketBuilderStep<UdpHeader> {
+        self.state.transport_header = Some(TransportHeader::Udp(UdpHeader {
+            source_port,
+            destination_port,
+            length: 0,   //calculated later
+            checksum: 0, //calculated later
+        }));
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<UdpHeader> {},
+        }
+    }
+
+    /// Adds a simple TCP header.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::PacketBuilder;
+    /// #
+    /// let builder = PacketBuilder::
+    ///     ethernet2([1,2,3,4,5,6],     // source mac
+    ///               [7,8,9,10,11,12]) // destination mac
+    ///    .ipv4([192,168,1,1], // source ip
+    ///          [192,168,1,2], // destination ip
+    ///          20)            // time to life
+    ///    .tcp(21,    // source port
+    ///         12,    // destination port
+    ///         12345, // sequence number
+    ///         4000); // window size
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn tcp(
+        mut self,
+        source_port: u16,
+        destination_port: u16,
+        sequence_number: u32,
+        window_size: u16,
+    ) -> PacketBuilderStep<TcpHeader> {
+        self.state.transport_header = Some(TransportHeader::Tcp(TcpHeader::new(
+            source_port,
+            destination_port,
+            sequence_number,
+            window_size,
+        )));
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<TcpHeader> {},
+        }
+    }
+
+    /// Adds a more complicated TCP header.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::PacketBuilder;
+    /// use etherparse::TcpHeader;
+    ///
+    /// let mut tcp_header = TcpHeader::new(
+    ///     21,     // source port
+    ///     12,     // destination port
+    ///     12345,  // sequence number
+    ///     4000,   // window size
+    /// );
+    /// tcp_header.psh = true;
+    /// tcp_header.ack = true;
+    /// tcp_header.acknowledgment_number = 1;
+    ///
+    /// let builder = PacketBuilder::
+    ///     ethernet2([1,2,3,4,5,6],     // source mac
+    ///               [7,8,9,10,11,12]) // destination mac
+    ///    .ipv4([192,168,1,1], // source ip
+    ///          [192,168,1,2], // destination ip
+    ///          20)            // time to life
+    ///    .tcp_header(tcp_header);
+    ///
+    /// //payload of the udp packet
+    /// let payload = [1,2,3,4,5,6,7,8];
+    ///
+    /// //get some memory to store the result
+    /// let mut result = Vec::<u8>::with_capacity(
+    ///                     builder.size(payload.len()));
+    ///
+    /// //serialize
+    /// builder.write(&mut result, &payload).unwrap();
+    /// ```
+    pub fn tcp_header(mut self, tcp_header: TcpHeader) -> PacketBuilderStep<TcpHeader> {
+        self.state.transport_header = Some(TransportHeader::Tcp(tcp_header));
+        //return for next step
+        PacketBuilderStep {
+            state: self.state,
+            _marker: marker::PhantomData::<TcpHeader> {},
+        }
+    }
+
+    /// Write all the headers and the payload with the given ip number.
+    ///
+    /// `last_next_header_ip_number` will be set in the last extension header
+    /// or if no extension header exists the ip header as the "next header" or
+    /// "protocol number".
+    pub fn write<T: io::Write + Sized>(
+        mut self,
+        writer: &mut T,
+        last_next_header_ip_number: IpNumber,
+        payload: &[u8],
+    ) -> Result<(), BuildWriteError> {
+        self.state
+            .ip_header
+            .as_mut()
+            .unwrap()
+            .set_next_headers(last_next_header_ip_number);
+        final_write(self, writer, payload)
+    }
+
+    ///Returns the size of the packet when it is serialized
+    pub fn size(&self, payload_size: usize) -> usize {
+        final_size(self, payload_size)
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl PacketBuilderStep<Icmpv4Header> {
+    /// Write all the headers and the payload.
+    pub fn write<T: io::Write + Sized>(
+        self,
+        writer: &mut T,
+        payload: &[u8],
+    ) -> Result<(), BuildWriteError> {
+        final_write(self, writer, payload)
+    }
+
+    /// Returns the size of the packet when it is serialized
+    pub fn size(&self, payload_size: usize) -> usize {
+        final_size(self, payload_size)
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl PacketBuilderStep<Icmpv6Header> {
+    ///Write all the headers and the payload.
+    pub fn write<T: io::Write + Sized>(
+        self,
+        writer: &mut T,
+        payload: &[u8],
+    ) -> Result<(), BuildWriteError> {
+        final_write(self, writer, payload)
+    }
+
+    ///Returns the size of the packet when it is serialized
+    pub fn size(&self, payload_size: usize) -> usize {
+        final_size(self, payload_size)
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl PacketBuilderStep<UdpHeader> {
+    ///Write all the headers and the payload.
+    pub fn write<T: io::Write + Sized>(
+        self,
+        writer: &mut T,
+        payload: &[u8],
+    ) -> Result<(), BuildWriteError> {
+        final_write(self, writer, payload)
+    }
+
+    ///Returns the size of the packet when it is serialized
+    pub fn size(&self, payload_size: usize) -> usize {
+        final_size(self, payload_size)
+    }
+}
+
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl PacketBuilderStep<TcpHeader> {
+    ///Set ns flag (ECN-nonce - concealment protection; experimental: see RFC 3540)
+    pub fn ns(mut self) -> PacketBuilderStep<TcpHeader> {
+        self.state
+            .transport_header
+            .as_mut()
+            .unwrap()
+            .mut_tcp()
+            .unwrap()
+            .ns = true;
+        self
+    }
+    ///Set fin flag (No more data from sender)
+    pub fn fin(mut self) -> PacketBuilderStep<TcpHeader> {
+        self.state
+            .transport_header
+            .as_mut()
+            .unwrap()
+            .mut_tcp()
+            .unwrap()
+            .fin = true;
+        self
+    }
+    ///Set the syn flag (synchronize sequence numbers)
+    pub fn syn(mut self) -> PacketBuilderStep<TcpHeader> {
+        self.state
+            .transport_header
+            .as_mut()
+            .unwrap()
+            .mut_tcp()
+            .unwrap()
+            .syn = true;
+        self
+    }
+    ///Sets the rst flag (reset the connection)
+    pub fn rst(mut self) -> PacketBuilderStep<TcpHeader> {
+        self.state
+            .transport_header
+            .as_mut()
+            .unwrap()
+            .mut_tcp()
+            .unwrap()
+            .rst = true;
+        self
+    }
+    ///Sets the psh flag (push function)
+    pub fn psh(mut self) -> PacketBuilderStep<TcpHeader> {
+        self.state
+            .transport_header
+            .as_mut()
+            .unwrap()
+            .mut_tcp()
+            .unwrap()
+            .psh = true;
+        self
+    }
+    ///Sets the ack flag and the acknowledgment_number.
+    pub fn ack(mut self, acknowledgment_number: u32) -> PacketBuilderStep<TcpHeader> {
+        {
+            let header = self
+                .state
+                .transport_header
+                .as_mut()
+                .unwrap()
+                .mut_tcp()
+                .unwrap();
+            header.ack = true;
+            header.acknowledgment_number = acknowledgment_number;
+        }
+        self
+    }
+    ///Set the urg flag & the urgent pointer field.
+    ///
+    ///The urgent pointer points to the sequence number of the octet following
+    ///the urgent data.
+    pub fn urg(mut self, urgent_pointer: u16) -> PacketBuilderStep<TcpHeader> {
+        {
+            let header = self
+                .state
+                .transport_header
+                .as_mut()
+                .unwrap()
+                .mut_tcp()
+                .unwrap();
+            header.urg = true;
+            header.urgent_pointer = urgent_pointer;
+        }
+        self
+    }
+    ///Sets ece flag (ECN-Echo, RFC 3168)
+    pub fn ece(mut self) -> PacketBuilderStep<TcpHeader> {
+        self.state
+            .transport_header
+            .as_mut()
+            .unwrap()
+            .mut_tcp()
+            .unwrap()
+            .ece = true;
+        self
+    }
+
+    ///Set cwr flag (Congestion Window Reduced)
+    ///
+    ///This flag is set by the sending host to indicate that it received a TCP segment with the ECE flag set and had responded in congestion control mechanism (added to header by RFC 3168).
+    pub fn cwr(mut self) -> PacketBuilderStep<TcpHeader> {
+        self.state
+            .transport_header
+            .as_mut()
+            .unwrap()
+            .mut_tcp()
+            .unwrap()
+            .cwr = true;
+        self
+    }
+
+    ///Set the tcp options of the header.
+    pub fn options(
+        mut self,
+        options: &[TcpOptionElement],
+    ) -> Result<PacketBuilderStep<TcpHeader>, TcpOptionWriteError> {
+        self.state
+            .transport_header
+            .as_mut()
+            .unwrap()
+            .mut_tcp()
+            .unwrap()
+            .set_options(options)?;
+        Ok(self)
+    }
+
+    ///Set the tcp options of the header (setting the bytes directly).
+    pub fn options_raw(
+        mut self,
+        options: &[u8],
+    ) -> Result<PacketBuilderStep<TcpHeader>, TcpOptionWriteError> {
+        self.state
+            .transport_header
+            .as_mut()
+            .unwrap()
+            .mut_tcp()
+            .unwrap()
+            .set_options_raw(options)?;
+        Ok(self)
+    }
+
+    ///Write all the headers and the payload.
+    pub fn write<T: io::Write + Sized>(
+        self,
+        writer: &mut T,
+        payload: &[u8],
+    ) -> Result<(), BuildWriteError> {
+        final_write(self, writer, payload)
+    }
+
+    ///Returns the size of the packet when it is serialized
+    pub fn size(&self, payload_size: usize) -> usize {
+        final_size(self, payload_size)
+    }
+}
+
+/// Write all the headers and the payload.
+fn final_write<T: io::Write + Sized, B>(
+    builder: PacketBuilderStep<B>,
+    writer: &mut T,
+    payload: &[u8],
+) -> Result<(), BuildWriteError> {
+    use BuildWriteError::*;
+
+    let ip_ether_type = {
+        use crate::IpHeaders::*;
+        match builder.state.ip_header {
+            Some(Ipv4(_, _)) => ether_type::IPV4,
+            Some(Ipv6(_, _)) => ether_type::IPV6,
+            None => panic!("Missing ip header"),
+        }
+    };
+
+    //link header
+    if let Some(link) = builder.state.link_header {
+        match link {
+            LinkHeader::Ethernet2(mut eth) => {
+                eth.ether_type = {
+                    use crate::VlanHeader::*;
+                    //determine the ether type depending on if there is a vlan tagging header
+                    match builder.state.vlan_header {
+                        Some(Single(_)) => ether_type::VLAN_TAGGED_FRAME,
+                        Some(Double(_)) => ether_type::PROVIDER_BRIDGING,
+                        //if no vlan header exists, the id is purely defined by the ip type
+                        None => ip_ether_type,
+                    }
+                };
+                eth.write(writer).map_err(Io)?;
+            }
+            LinkHeader::LinuxSll(mut linux_sll) => {
+                // Assumes that next layers are ether based. If more types of
+                // layers are supported, this should be updated
+                debug_assert_eq!(linux_sll.arp_hrd_type, ArpHardwareId::ETHER);
+
+                linux_sll.protocol_type.change_value(ip_ether_type.into());
+                linux_sll.write(writer).map_err(Io)?;
+            }
+        }
+    }
+
+    //write the vlan header if it exists
+    use crate::VlanHeader::*;
+    match builder.state.vlan_header {
+        Some(Single(mut value)) => {
+            //set ether types
+            value.ether_type = ip_ether_type;
+            //serialize
+            value.write(writer).map_err(Io)?;
+        }
+        Some(Double(mut value)) => {
+            //set ether types
+            value.outer.ether_type = ether_type::VLAN_TAGGED_FRAME;
+            value.inner.ether_type = ip_ether_type;
+            //serialize
+            value.write(writer).map_err(Io)?;
+        }
+        None => {}
+    }
+
+    //ip header
+    use crate::IpHeaders::*;
+    let ip_header = builder.state.ip_header.unwrap();
+
+    //transport header
+    let transport = builder.state.transport_header;
+    match transport {
+        None => {
+            // in case no transport header is present the protocol
+            // number and next_header fields are set in the write call
+            // directly and don't need to be set here again.
+            match ip_header {
+                Ipv4(mut ip, ext) => {
+                    ip.set_payload_len(ext.header_len() + payload.len())
+                        .map_err(PayloadLen)?;
+                    ip.write(writer).map_err(Io)?;
+                    ext.write(writer, ip.protocol).map_err(|err| {
+                        use err::ipv4_exts::HeaderWriteError as I;
+                        match err {
+                            I::Io(err) => Io(err),
+                            I::Content(err) => Ipv4Exts(err),
+                        }
+                    })?;
+                }
+                Ipv6(mut ip, ext) => {
+                    ip.set_payload_length(ext.header_len() + payload.len())
+                        .map_err(PayloadLen)?;
+                    ip.write(writer).map_err(Io)?;
+                    ext.write(writer, ip.next_header).map_err(|err| {
+                        use err::ipv6_exts::HeaderWriteError as I;
+                        match err {
+                            I::Io(err) => Io(err),
+                            I::Content(err) => Ipv6Exts(err),
+                        }
+                    })?;
+                }
+            }
+        }
+        Some(mut transport) => {
+            match ip_header {
+                Ipv4(mut ip, mut ext) => {
+                    //set total length & udp payload length (ip checks that the payload length is ok)
+                    let transport_size = transport.header_len() + payload.len();
+                    ip.set_payload_len(ext.header_len() + transport_size)
+                        .map_err(PayloadLen)?;
+                    use crate::TransportHeader::*;
+                    match transport {
+                        Icmpv4(_) => {}
+                        Icmpv6(_) => {}
+                        Udp(ref mut udp) => {
+                            udp.length = transport_size as u16;
+                        }
+                        Tcp(_) => {}
+                    }
+
+                    //ip protocol number & next header values of the extension header
+                    ip.protocol = ext.set_next_headers(match transport {
+                        Icmpv4(_) => ip_number::ICMP,
+                        Icmpv6(_) => ip_number::IPV6_ICMP,
+                        Udp(_) => ip_number::UDP,
+                        Tcp(_) => ip_number::TCP,
+                    });
+
+                    //calculate the udp checksum
+                    transport
+                        .update_checksum_ipv4(&ip, payload)
+                        .map_err(|err| {
+                            use err::packet::TransportChecksumError as I;
+                            match err {
+                                I::PayloadLen(err) => PayloadLen(err),
+                                I::Icmpv6InIpv4 => Icmpv6InIpv4,
+                            }
+                        })?;
+
+                    //write (will automatically calculate the checksum)
+                    ip.write(writer).map_err(Io)?;
+                    ext.write(writer, ip.protocol).map_err(|err| {
+                        use err::ipv4_exts::HeaderWriteError as I;
+                        match err {
+                            I::Io(err) => Io(err),
+                            I::Content(err) => Ipv4Exts(err),
+                        }
+                    })?;
+                }
+                Ipv6(mut ip, mut ext) => {
+                    //set total length
+                    let transport_size = transport.header_len() + payload.len();
+                    ip.set_payload_length(ext.header_len() + transport_size)
+                        .map_err(PayloadLen)?;
+                    use crate::TransportHeader::*;
+                    match transport {
+                        Icmpv4(_) => {}
+                        Icmpv6(_) => {}
+                        Udp(ref mut udp) => {
+                            udp.length = transport_size as u16;
+                        }
+                        Tcp(_) => {}
+                    }
+
+                    //set the protocol
+                    ip.next_header = ext.set_next_headers(match transport {
+                        Icmpv4(_) => ip_number::ICMP,
+                        Icmpv6(_) => ip_number::IPV6_ICMP,
+                        Udp(_) => ip_number::UDP,
+                        Tcp(_) => ip_number::TCP,
+                    });
+
+                    //calculate the udp checksum
+                    transport
+                        .update_checksum_ipv6(&ip, payload)
+                        .map_err(PayloadLen)?;
+
+                    //write (will automatically calculate the checksum)
+                    ip.write(writer).map_err(Io)?;
+                    ext.write(writer, ip.next_header).map_err(|err| {
+                        use err::ipv6_exts::HeaderWriteError as I;
+                        match err {
+                            I::Io(err) => Io(err),
+                            I::Content(err) => Ipv6Exts(err),
+                        }
+                    })?;
+                }
+            }
+
+            //finally write the udp header & payload
+            transport.write(writer).map_err(Io)?;
+        }
+    }
+    writer.write_all(payload).map_err(Io)?;
+    Ok(())
+}
+
+///Returns the size of the packet when it is serialized
+fn final_size<B>(builder: &PacketBuilderStep<B>, payload_size: usize) -> usize {
+    use crate::IpHeaders::*;
+    use crate::TransportHeader::*;
+    use crate::VlanHeader::*;
+    (match builder.state.link_header {
+        Some(ref header) => header.header_len(),
+        None => 0,
+    }) + match builder.state.vlan_header {
+        Some(Single(_)) => SingleVlanHeader::LEN,
+        Some(Double(_)) => DoubleVlanHeader::LEN,
+        None => 0,
+    } + match builder.state.ip_header {
+        Some(Ipv4(ref value, ref ext)) => value.header_len() + ext.header_len(),
+        Some(Ipv6(_, ref ext)) => Ipv6Header::LEN + ext.header_len(),
+        None => 0,
+    } + match builder.state.transport_header {
+        Some(Icmpv4(ref value)) => value.header_len(),
+        Some(Icmpv6(ref value)) => value.header_len(),
+        Some(Udp(_)) => UdpHeader::LEN,
+        Some(Tcp(ref value)) => value.header_len(),
+        None => 0,
+    } + payload_size
+}
+
+#[cfg(test)]
+mod white_box_tests {
+    use super::*;
+    use alloc::vec::Vec;
+
+    //white box tests that need internal access
+    #[test]
+    fn size() {
+        assert_eq!(
+            0,
+            PacketBuilderStep::<UdpHeader> {
+                state: PacketImpl {
+                    link_header: None,
+                    ip_header: None,
+                    vlan_header: None,
+                    transport_header: None
+                },
+                _marker: marker::PhantomData::<UdpHeader> {}
+            }
+            .size(0)
+        );
+    }
+
+    #[test]
+    #[should_panic]
+    fn final_write_panic_missing_ip() {
+        let mut writer = Vec::new();
+        final_write(
+            PacketBuilderStep::<UdpHeader> {
+                state: PacketImpl {
+                    link_header: None,
+                    ip_header: None,
+                    vlan_header: None,
+                    transport_header: None,
+                },
+                _marker: marker::PhantomData::<UdpHeader> {},
+            },
+            &mut writer,
+            &[],
+        )
+        .unwrap();
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::{vec, vec::Vec};
+    use proptest::prelude::*;
+    use std::io::Read;
+
+    #[test]
+    fn eth_ipv4_udp() {
+        //generate
+        let in_payload = [24, 25, 26, 27];
+        let mut serialized = Vec::new();
+        PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12])
+            .ipv4([13, 14, 15, 16], [17, 18, 19, 20], 21)
+            .udp(22, 23)
+            .write(&mut serialized, &in_payload)
+            .unwrap();
+
+        //check the deserialized size
+        let expected_ip_size: usize = UdpHeader::LEN + in_payload.len();
+        assert_eq!(
+            expected_ip_size + Ethernet2Header::LEN + Ipv4Header::MIN_LEN,
+            serialized.len()
+        );
+
+        //deserialize and check that everything is as expected
+        use std::io::Cursor;
+        //deserialize each part of the message and check it
+        let mut cursor = Cursor::new(&serialized);
+
+        //ethernet 2 header
+        assert_eq!(
+            Ethernet2Header::read(&mut cursor).unwrap(),
+            Ethernet2Header {
+                source: [1, 2, 3, 4, 5, 6],
+                destination: [7, 8, 9, 10, 11, 12],
+                ether_type: ether_type::IPV4
+            }
+        );
+
+        //ip header
+        let ip_actual = Ipv4Header::read(&mut cursor).unwrap();
+        let mut ip_expected = Ipv4Header::new(
+            expected_ip_size as u16,
+            21, //ttl
+            ip_number::UDP,
+            [13, 14, 15, 16],
+            [17, 18, 19, 20],
+        )
+        .unwrap();
+        ip_expected.header_checksum = ip_expected.calc_header_checksum();
+        assert_eq!(ip_actual, ip_expected);
+
+        //udp header
+        let udp_actual = UdpHeader::read(&mut cursor).unwrap();
+        let udp_expected =
+            UdpHeader::with_ipv4_checksum(22, 23, &ip_expected, &in_payload).unwrap();
+        assert_eq!(udp_actual, udp_expected);
+
+        //payload
+        let mut actual_payload: [u8; 4] = [0; 4];
+        cursor.read_exact(&mut actual_payload).unwrap();
+        assert_eq!(actual_payload, in_payload);
+    }
+
+    #[test]
+    fn linuxsll_ipv4_udp() {
+        //generate
+        let in_payload = [24, 25, 26, 27];
+        let mut serialized = Vec::new();
+        PacketBuilder::linux_sll(LinuxSllPacketType::OUTGOING, 6, [7, 8, 9, 10, 11, 12, 0, 0])
+            .ipv4([13, 14, 15, 16], [17, 18, 19, 20], 21)
+            .udp(22, 23)
+            .write(&mut serialized, &in_payload)
+            .unwrap();
+
+        //check the deserialized size
+        let expected_ip_size: usize = UdpHeader::LEN + in_payload.len();
+        assert_eq!(
+            expected_ip_size + LinuxSllHeader::LEN + Ipv4Header::MIN_LEN,
+            serialized.len()
+        );
+
+        //deserialize and check that everything is as expected
+        use std::io::Cursor;
+        //deserialize each part of the message and check it
+        let mut cursor = Cursor::new(&serialized);
+
+        //ethernet 2 header
+        assert_eq!(
+            LinuxSllHeader::read(&mut cursor).unwrap(),
+            LinuxSllHeader {
+                packet_type: LinuxSllPacketType::OUTGOING,
+                arp_hrd_type: ArpHardwareId::ETHER,
+                sender_address_valid_length: 6,
+                sender_address: [7, 8, 9, 10, 11, 12, 0, 0],
+                protocol_type: LinuxSllProtocolType::EtherType(EtherType::IPV4)
+            }
+        );
+
+        //ip header
+        let ip_actual = Ipv4Header::read(&mut cursor).unwrap();
+        let mut ip_expected = Ipv4Header::new(
+            expected_ip_size as u16,
+            21, //ttl
+            ip_number::UDP,
+            [13, 14, 15, 16],
+            [17, 18, 19, 20],
+        )
+        .unwrap();
+        ip_expected.header_checksum = ip_expected.calc_header_checksum();
+        assert_eq!(ip_actual, ip_expected);
+
+        //udp header
+        let udp_actual = UdpHeader::read(&mut cursor).unwrap();
+        let udp_expected =
+            UdpHeader::with_ipv4_checksum(22, 23, &ip_expected, &in_payload).unwrap();
+        assert_eq!(udp_actual, udp_expected);
+
+        //payload
+        let mut actual_payload: [u8; 4] = [0; 4];
+        cursor.read_exact(&mut actual_payload).unwrap();
+        assert_eq!(actual_payload, in_payload);
+    }
+
+    #[test]
+    fn ipv4() {
+        let auth_ext = IpAuthHeader::new(0.into(), 1, 2, &[3, 4, 5, 6]).unwrap();
+
+        //generate
+        let in_payload = [22, 23, 24, 25];
+        let mut serialized = Vec::new();
+        let builder = PacketBuilder::ip(IpHeaders::Ipv4(
+            Ipv4Header::new(
+                in_payload.len() as u16,
+                21,
+                0.into(),
+                [13, 14, 15, 16],
+                [17, 18, 19, 20],
+            )
+            .unwrap(),
+            Ipv4Extensions {
+                auth: Some(auth_ext.clone()),
+            },
+        ));
+
+        // check size
+        assert_eq!(
+            builder.size(in_payload.len()),
+            Ipv4Header::MIN_LEN + auth_ext.header_len() + in_payload.len()
+        );
+
+        // write
+        serialized.reserve(builder.size(in_payload.len()));
+        builder
+            .write(&mut serialized, 200.into(), &in_payload)
+            .unwrap();
+
+        //check the deserialized size
+        assert_eq!(
+            Ipv4Header::MIN_LEN + auth_ext.header_len() + in_payload.len(),
+            serialized.len()
+        );
+
+        //deserialize and check that everything is as expected
+        use std::io::{Cursor, Read};
+
+        //deserialize each part of the message and check it
+        let mut cursor = Cursor::new(&serialized);
+
+        //ip header
+        let ip_actual = Ipv4Header::read(&mut cursor).unwrap();
+        let mut ip_expected = Ipv4Header::new(
+            (auth_ext.header_len() + in_payload.len()) as u16,
+            21,              //ttl
+            ip_number::AUTH, // should have been set
+            [13, 14, 15, 16],
+            [17, 18, 19, 20],
+        )
+        .unwrap();
+        ip_expected.header_checksum = ip_expected.calc_header_checksum();
+        assert_eq!(ip_actual, ip_expected);
+
+        // auth header
+        let auth_actual = IpAuthHeader::read(&mut cursor).unwrap();
+        assert_eq!(
+            auth_actual,
+            IpAuthHeader::new(
+                200.into(), // ip number should have been set
+                1,
+                2,
+                &[3, 4, 5, 6]
+            )
+            .unwrap()
+        );
+
+        //payload
+        let mut actual_payload: [u8; 4] = [0; 4];
+        cursor.read_exact(&mut actual_payload).unwrap();
+        assert_eq!(actual_payload, in_payload);
+    }
+
+    #[test]
+    fn ipv6() {
+        let auth_ext = IpAuthHeader::new(0.into(), 1, 2, &[3, 4, 5, 6]).unwrap();
+
+        //generate
+        let in_payload = [48, 49, 50, 51];
+        let mut serialized = Vec::new();
+        let builder = PacketBuilder::ip(IpHeaders::Ipv6(
+            Ipv6Header {
+                traffic_class: 0,
+                flow_label: Ipv6FlowLabel::ZERO,
+                payload_length: in_payload.len() as u16,
+                next_header: 0.into(),
+                hop_limit: 47,
+                source: [
+                    11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26,
+                ],
+                destination: [
+                    31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+                ],
+            },
+            Ipv6Extensions {
+                hop_by_hop_options: None,
+                destination_options: None,
+                routing: None,
+                fragment: None,
+                auth: Some(auth_ext.clone()),
+            },
+        ));
+
+        // check size
+        assert_eq!(
+            builder.size(in_payload.len()),
+            Ipv6Header::LEN + auth_ext.header_len() + in_payload.len()
+        );
+
+        // write
+        builder
+            .write(&mut serialized, 200.into(), &in_payload)
+            .unwrap();
+
+        //check the deserialized size
+        assert_eq!(
+            Ipv6Header::LEN + auth_ext.header_len() + in_payload.len(),
+            serialized.len()
+        );
+
+        //deserialize and check that everything is as expected
+        use std::io::{Cursor, Read};
+
+        //deserialize each part of the message and check it
+        let mut cursor = Cursor::new(&serialized);
+
+        //ip header
+        let ip_actual = Ipv6Header::read(&mut cursor).unwrap();
+        let ip_expected = Ipv6Header {
+            traffic_class: 0,
+            flow_label: Ipv6FlowLabel::ZERO,
+            payload_length: (auth_ext.header_len() + in_payload.len()) as u16,
+            next_header: ip_number::AUTH, // should have been set
+            hop_limit: 47,
+            source: [
+                11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26,
+            ],
+            destination: [
+                31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+            ],
+        };
+
+        assert_eq!(ip_actual, ip_expected);
+
+        // auth header
+        let auth_actual = IpAuthHeader::read(&mut cursor).unwrap();
+        assert_eq!(
+            auth_actual,
+            IpAuthHeader::new(
+                200.into(), // ip number should have been set
+                1,
+                2,
+                &[3, 4, 5, 6]
+            )
+            .unwrap()
+        );
+
+        //payload
+        let mut actual_payload: [u8; 4] = [0; 4];
+        cursor.read_exact(&mut actual_payload).unwrap();
+        assert_eq!(actual_payload, in_payload);
+    }
+
+    #[test]
+    fn ipv4_udp() {
+        //generate
+        let in_payload = [24, 25, 26, 27];
+        let mut serialized = Vec::new();
+        PacketBuilder::ipv4([13, 14, 15, 16], [17, 18, 19, 20], 21)
+            .udp(22, 23)
+            .write(&mut serialized, &in_payload)
+            .unwrap();
+
+        //check the deserialized size
+        let expected_ip_size: usize = UdpHeader::LEN + in_payload.len();
+        assert_eq!(expected_ip_size + Ipv4Header::MIN_LEN, serialized.len());
+
+        //deserialize and check that everything is as expected
+        use std::io::{Cursor, Read};
+
+        //deserialize each part of the message and check it
+        let mut cursor = Cursor::new(&serialized);
+
+        //ip header
+        let ip_actual = Ipv4Header::read(&mut cursor).unwrap();
+        let mut ip_expected = Ipv4Header::new(
+            expected_ip_size as u16,
+            21, //ttl
+            ip_number::UDP,
+            [13, 14, 15, 16],
+            [17, 18, 19, 20],
+        )
+        .unwrap();
+        ip_expected.header_checksum = ip_expected.calc_header_checksum();
+        assert_eq!(ip_actual, ip_expected);
+
+        //udp header
+        let udp_actual = UdpHeader::read(&mut cursor).unwrap();
+        let udp_expected =
+            UdpHeader::with_ipv4_checksum(22, 23, &ip_expected, &in_payload).unwrap();
+        assert_eq!(udp_actual, udp_expected);
+
+        //payload
+        let mut actual_payload: [u8; 4] = [0; 4];
+        cursor.read_exact(&mut actual_payload).unwrap();
+        assert_eq!(actual_payload, in_payload);
+    }
+
+    #[test]
+    fn ipv6_udp() {
+        //generate
+        let in_payload = [24, 25, 26, 27];
+        let mut serialized = Vec::new();
+        PacketBuilder::ipv6(
+            //source
+            [
+                11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26,
+            ],
+            //destination
+            [
+                31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+            ],
+            //hop_limit
+            47,
+        )
+        .udp(22, 23)
+        .write(&mut serialized, &in_payload)
+        .unwrap();
+
+        //check the deserialized size
+        let expected_ip_size: usize = UdpHeader::LEN + in_payload.len();
+        assert_eq!(expected_ip_size + Ipv6Header::LEN, serialized.len());
+
+        //deserialize and check that everything is as expected
+        use std::io::{Cursor, Read};
+
+        //deserialize each part of the message and check it
+        let mut cursor = Cursor::new(&serialized);
+
+        //ip header
+        let ip_actual = Ipv6Header::read(&mut cursor).unwrap();
+        let ip_expected = Ipv6Header {
+            traffic_class: 0,
+            flow_label: Ipv6FlowLabel::ZERO,
+            payload_length: (UdpHeader::LEN + in_payload.len()) as u16,
+            next_header: ip_number::UDP,
+            hop_limit: 47,
+            source: [
+                11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26,
+            ],
+            destination: [
+                31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+            ],
+        };
+
+        assert_eq!(ip_actual, ip_expected);
+
+        //udp header
+        let udp_actual = UdpHeader::read(&mut cursor).unwrap();
+        let udp_expected =
+            UdpHeader::with_ipv6_checksum(22, 23, &ip_expected, &in_payload).unwrap();
+        assert_eq!(udp_actual, udp_expected);
+
+        //payload
+        let mut actual_payload: [u8; 4] = [0; 4];
+        cursor.read_exact(&mut actual_payload).unwrap();
+        assert_eq!(actual_payload, in_payload);
+    }
+
+    #[test]
+    fn ipv4_custom_udp() {
+        //generate
+        let in_payload = [24, 25, 26, 27];
+        let mut serialized = Vec::new();
+        PacketBuilder::ip(IpHeaders::Ipv4(
+            Ipv4Header::new(
+                0,                //payload_len will be replaced during write
+                12,               //time_to_live
+                ip_number::TCP,   //will be replaced during write
+                [13, 14, 15, 16], //source
+                [17, 18, 19, 20], //destination
+            )
+            .unwrap(),
+            Default::default(),
+        ))
+        .udp(22, 23)
+        .write(&mut serialized, &in_payload)
+        .unwrap();
+
+        //check the deserialized size
+        let expected_ip_size: usize = UdpHeader::LEN + in_payload.len();
+        assert_eq!(expected_ip_size + Ipv4Header::MIN_LEN, serialized.len());
+
+        //deserialize and check that everything is as expected
+        use std::io::{Cursor, Read};
+
+        //deserialize each part of the message and check it
+        let mut cursor = Cursor::new(&serialized);
+
+        //ip header
+        let ip_actual = Ipv4Header::read(&mut cursor).unwrap();
+        let mut ip_expected = Ipv4Header::new(
+            expected_ip_size as u16,
+            12, //ttl
+            ip_number::UDP,
+            [13, 14, 15, 16],
+            [17, 18, 19, 20],
+        )
+        .unwrap();
+        ip_expected.header_checksum = ip_expected.calc_header_checksum();
+        assert_eq!(ip_actual, ip_expected);
+
+        //udp header
+        let udp_actual = UdpHeader::read(&mut cursor).unwrap();
+        let udp_expected =
+            UdpHeader::with_ipv4_checksum(22, 23, &ip_expected, &in_payload).unwrap();
+        assert_eq!(udp_actual, udp_expected);
+
+        //payload
+        let mut actual_payload: [u8; 4] = [0; 4];
+        cursor.read_exact(&mut actual_payload).unwrap();
+        assert_eq!(actual_payload, in_payload);
+    }
+
+    #[test]
+    fn udp_builder_eth_ipv6_udp() {
+        //generate
+        let in_payload = [50, 51, 52, 53];
+        let mut serialized = Vec::new();
+        PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12])
+            .ipv6(
+                [
+                    11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26,
+                ],
+                [
+                    31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+                ],
+                47,
+            )
+            .udp(48, 49)
+            .write(&mut serialized, &in_payload)
+            .unwrap();
+
+        //check the deserialized size
+        assert_eq!(
+            Ethernet2Header::LEN + Ipv6Header::LEN + UdpHeader::LEN + in_payload.len(),
+            serialized.len()
+        );
+
+        //deserialize and check that everything is as expected
+        use std::io::Cursor;
+        use std::io::Read;
+        //deserialize each part of the message and check it
+        let mut cursor = Cursor::new(&serialized);
+
+        //ethernet 2 header
+        assert_eq!(
+            Ethernet2Header::read(&mut cursor).unwrap(),
+            Ethernet2Header {
+                source: [1, 2, 3, 4, 5, 6],
+                destination: [7, 8, 9, 10, 11, 12],
+                ether_type: ether_type::IPV6
+            }
+        );
+
+        //ip header
+        let ip_actual = Ipv6Header::read(&mut cursor).unwrap();
+        let ip_expected = Ipv6Header {
+            traffic_class: 0,
+            flow_label: Ipv6FlowLabel::ZERO,
+            payload_length: (UdpHeader::LEN + in_payload.len()) as u16,
+            next_header: ip_number::UDP,
+            hop_limit: 47,
+            source: [
+                11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26,
+            ],
+            destination: [
+                31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+            ],
+        };
+        assert_eq!(ip_actual, ip_expected);
+
+        //udp header
+        let udp_actual = UdpHeader::read(&mut cursor).unwrap();
+        let udp_expected =
+            UdpHeader::with_ipv6_checksum(48, 49, &ip_expected, &in_payload).unwrap();
+        assert_eq!(udp_actual, udp_expected);
+
+        //payload
+        let mut actual_payload: [u8; 4] = [0; 4];
+        cursor.read_exact(&mut actual_payload).unwrap();
+        assert_eq!(actual_payload, in_payload);
+    }
+
+    #[test]
+    fn udp_builder_linuxsll_ipv6_udp() {
+        //generate
+        let in_payload = [50, 51, 52, 53];
+        let mut serialized = Vec::new();
+        PacketBuilder::linux_sll(LinuxSllPacketType::OUTGOING, 6, [7, 8, 9, 10, 11, 12, 0, 0])
+            .ipv6(
+                [
+                    11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26,
+                ],
+                [
+                    31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+                ],
+                47,
+            )
+            .udp(48, 49)
+            .write(&mut serialized, &in_payload)
+            .unwrap();
+
+        //check the deserialized size
+        assert_eq!(
+            LinuxSllHeader::LEN + Ipv6Header::LEN + UdpHeader::LEN + in_payload.len(),
+            serialized.len()
+        );
+
+        //deserialize and check that everything is as expected
+        use std::io::Cursor;
+        use std::io::Read;
+        //deserialize each part of the message and check it
+        let mut cursor = Cursor::new(&serialized);
+
+        //ethernet 2 header
+        assert_eq!(
+            LinuxSllHeader::read(&mut cursor).unwrap(),
+            LinuxSllHeader {
+                packet_type: LinuxSllPacketType::OUTGOING,
+                arp_hrd_type: ArpHardwareId::ETHER,
+                sender_address_valid_length: 6,
+                sender_address: [7, 8, 9, 10, 11, 12, 0, 0],
+                protocol_type: LinuxSllProtocolType::EtherType(EtherType::IPV6)
+            }
+        );
+
+        //ip header
+        let ip_actual = Ipv6Header::read(&mut cursor).unwrap();
+        let ip_expected = Ipv6Header {
+            traffic_class: 0,
+            flow_label: Ipv6FlowLabel::ZERO,
+            payload_length: (UdpHeader::LEN + in_payload.len()) as u16,
+            next_header: ip_number::UDP,
+            hop_limit: 47,
+            source: [
+                11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26,
+            ],
+            destination: [
+                31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+            ],
+        };
+        assert_eq!(ip_actual, ip_expected);
+
+        //udp header
+        let udp_actual = UdpHeader::read(&mut cursor).unwrap();
+        let udp_expected =
+            UdpHeader::with_ipv6_checksum(48, 49, &ip_expected, &in_payload).unwrap();
+        assert_eq!(udp_actual, udp_expected);
+
+        //payload
+        let mut actual_payload: [u8; 4] = [0; 4];
+        cursor.read_exact(&mut actual_payload).unwrap();
+        assert_eq!(actual_payload, in_payload);
+    }
+
+    #[test]
+    fn udp_builder_eth_single_vlan_ipv4_udp() {
+        //generate
+        let in_payload = [50, 51, 52, 53];
+        let mut serialized = Vec::new();
+        PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12])
+            .single_vlan(0x123.try_into().unwrap())
+            .ipv4([13, 14, 15, 16], [17, 18, 19, 20], 21)
+            .udp(48, 49)
+            .write(&mut serialized, &in_payload)
+            .unwrap();
+
+        //check the deserialized size
+
+        //check the deserialized size
+        let expected_ip_size: usize = UdpHeader::LEN + in_payload.len();
+        assert_eq!(
+            expected_ip_size + Ethernet2Header::LEN + Ipv4Header::MIN_LEN + SingleVlanHeader::LEN,
+            serialized.len()
+        );
+
+        //deserialize and check that everything is as expected
+        use std::io::Cursor;
+        use std::io::Read;
+        //deserialize each part of the message and check it
+        let mut cursor = Cursor::new(&serialized);
+
+        //ethernet 2 header
+        assert_eq!(
+            Ethernet2Header::read(&mut cursor).unwrap(),
+            Ethernet2Header {
+                source: [1, 2, 3, 4, 5, 6],
+                destination: [7, 8, 9, 10, 11, 12],
+                ether_type: ether_type::VLAN_TAGGED_FRAME
+            }
+        );
+
+        //vlan header
+        assert_eq!(
+            SingleVlanHeader::read(&mut cursor).unwrap(),
+            SingleVlanHeader {
+                pcp: VlanPcp::ZERO,
+                drop_eligible_indicator: false,
+                vlan_id: 0x123.try_into().unwrap(),
+                ether_type: ether_type::IPV4
+            }
+        );
+
+        //ip header
+        let ip_actual = Ipv4Header::read(&mut cursor).unwrap();
+        let mut ip_expected = Ipv4Header::new(
+            expected_ip_size as u16, //payload_len
+            21,                      //ttl
+            ip_number::UDP,
+            [13, 14, 15, 16],
+            [17, 18, 19, 20],
+        )
+        .unwrap();
+        ip_expected.header_checksum = ip_expected.calc_header_checksum();
+        assert_eq!(ip_actual, ip_expected);
+
+        //udp header
+        let udp_actual = UdpHeader::read(&mut cursor).unwrap();
+        let udp_expected =
+            UdpHeader::with_ipv4_checksum(48, 49, &ip_expected, &in_payload).unwrap();
+        assert_eq!(udp_actual, udp_expected);
+
+        //payload
+        let mut actual_payload: [u8; 4] = [0; 4];
+        cursor.read_exact(&mut actual_payload).unwrap();
+        assert_eq!(actual_payload, in_payload);
+    }
+
+    #[test]
+    fn udp_builder_eth_double_vlan_ipv6_udp() {
+        //generate
+        let in_payload = [50, 51, 52, 53];
+        let mut serialized = Vec::new();
+        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(
+                [
+                    11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26,
+                ],
+                [
+                    31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+                ],
+                47,
+            )
+            .udp(48, 49)
+            .write(&mut serialized, &in_payload)
+            .unwrap();
+
+        //check the deserialized size
+        assert_eq!(
+            Ethernet2Header::LEN
+                + DoubleVlanHeader::LEN
+                + Ipv6Header::LEN
+                + UdpHeader::LEN
+                + in_payload.len(),
+            serialized.len()
+        );
+
+        //deserialize and check that everything is as expected
+        use std::io::Cursor;
+        use std::io::Read;
+
+        //deserialize each part of the message and check it
+        let mut cursor = Cursor::new(&serialized);
+
+        //ethernet 2 header
+        assert_eq!(
+            Ethernet2Header::read(&mut cursor).unwrap(),
+            Ethernet2Header {
+                source: [1, 2, 3, 4, 5, 6],
+                destination: [7, 8, 9, 10, 11, 12],
+                ether_type: ether_type::PROVIDER_BRIDGING,
+            }
+        );
+
+        //outer vlan header
+        assert_eq!(
+            SingleVlanHeader::read(&mut cursor).unwrap(),
+            SingleVlanHeader {
+                pcp: VlanPcp::ZERO,
+                drop_eligible_indicator: false,
+                vlan_id: 0x123.try_into().unwrap(),
+                ether_type: ether_type::VLAN_TAGGED_FRAME
+            }
+        );
+
+        //inner vlan header
+        assert_eq!(
+            SingleVlanHeader::read(&mut cursor).unwrap(),
+            SingleVlanHeader {
+                pcp: VlanPcp::ZERO,
+                drop_eligible_indicator: false,
+                vlan_id: 0x234.try_into().unwrap(),
+                ether_type: ether_type::IPV6
+            }
+        );
+
+        //ip header
+        let ip_actual = Ipv6Header::read(&mut cursor).unwrap();
+        let ip_expected = Ipv6Header {
+            traffic_class: 0,
+            flow_label: Ipv6FlowLabel::ZERO,
+            payload_length: (UdpHeader::LEN + in_payload.len()) as u16,
+            next_header: ip_number::UDP,
+            hop_limit: 47,
+            source: [
+                11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26,
+            ],
+            destination: [
+                31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+            ],
+        };
+        assert_eq!(ip_actual, ip_expected);
+
+        //udp header
+        let udp_actual = UdpHeader::read(&mut cursor).unwrap();
+        let udp_expected =
+            UdpHeader::with_ipv6_checksum(48, 49, &ip_expected, &in_payload).unwrap();
+        assert_eq!(udp_actual, udp_expected);
+
+        //payload
+        let mut actual_payload: [u8; 4] = [0; 4];
+        cursor.read_exact(&mut actual_payload).unwrap();
+        assert_eq!(actual_payload, in_payload);
+    }
+
+    #[test]
+    fn udp_builder_eth_ip_udp() {
+        //generate
+        let in_payload = [50, 51, 52, 53];
+        let mut serialized = Vec::new();
+        PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12])
+            .ip(IpHeaders::Ipv6(
+                Ipv6Header {
+                    traffic_class: 1,
+                    flow_label: 2.try_into().unwrap(),
+                    payload_length: (UdpHeader::LEN + in_payload.len()) as u16,
+                    next_header: ip_number::UDP,
+                    hop_limit: 47,
+                    source: [
+                        11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26,
+                    ],
+                    destination: [
+                        31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+                    ],
+                },
+                Default::default(),
+            ))
+            .udp(48, 49)
+            .write(&mut serialized, &in_payload)
+            .unwrap();
+
+        //check the deserialized size
+        assert_eq!(
+            Ethernet2Header::LEN + Ipv6Header::LEN + UdpHeader::LEN + in_payload.len(),
+            serialized.len()
+        );
+
+        //deserialize and check that everything is as expected
+        use std::io::Cursor;
+        use std::io::Read;
+
+        //deserialize each part of the message and check it
+        let mut cursor = Cursor::new(&serialized);
+
+        //ethernet 2 header
+        assert_eq!(
+            Ethernet2Header::read(&mut cursor).unwrap(),
+            Ethernet2Header {
+                source: [1, 2, 3, 4, 5, 6],
+                destination: [7, 8, 9, 10, 11, 12],
+                ether_type: ether_type::IPV6
+            }
+        );
+
+        //ip header
+        let ip_actual = Ipv6Header::read(&mut cursor).unwrap();
+        let ip_expected = Ipv6Header {
+            traffic_class: 1,
+            flow_label: 2.try_into().unwrap(),
+            payload_length: (UdpHeader::LEN + in_payload.len()) as u16,
+            next_header: ip_number::UDP,
+            hop_limit: 47,
+            source: [
+                11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26,
+            ],
+            destination: [
+                31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+            ],
+        };
+        assert_eq!(ip_actual, ip_expected);
+
+        //udp header
+        let udp_actual = UdpHeader::read(&mut cursor).unwrap();
+        let udp_expected =
+            UdpHeader::with_ipv6_checksum(48, 49, &ip_expected, &in_payload).unwrap();
+        assert_eq!(udp_actual, udp_expected);
+
+        //payload
+        let mut actual_payload: [u8; 4] = [0; 4];
+        cursor.read_exact(&mut actual_payload).unwrap();
+        assert_eq!(actual_payload, in_payload);
+    }
+
+    #[test]
+    fn udp_builder_linuxsll_ip_udp() {
+        //generate
+        let in_payload = [50, 51, 52, 53];
+        let mut serialized = Vec::new();
+        PacketBuilder::linux_sll(LinuxSllPacketType::OUTGOING, 6, [7, 8, 9, 10, 11, 12, 0, 0])
+            .ip(IpHeaders::Ipv6(
+                Ipv6Header {
+                    traffic_class: 1,
+                    flow_label: 2.try_into().unwrap(),
+                    payload_length: (UdpHeader::LEN + in_payload.len()) as u16,
+                    next_header: ip_number::UDP,
+                    hop_limit: 47,
+                    source: [
+                        11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26,
+                    ],
+                    destination: [
+                        31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+                    ],
+                },
+                Default::default(),
+            ))
+            .udp(48, 49)
+            .write(&mut serialized, &in_payload)
+            .unwrap();
+
+        //check the deserialized size
+        assert_eq!(
+            LinuxSllHeader::LEN + Ipv6Header::LEN + UdpHeader::LEN + in_payload.len(),
+            serialized.len()
+        );
+
+        //deserialize and check that everything is as expected
+        use std::io::Cursor;
+        use std::io::Read;
+
+        //deserialize each part of the message and check it
+        let mut cursor = Cursor::new(&serialized);
+
+        //ethernet 2 header
+        assert_eq!(
+            LinuxSllHeader::read(&mut cursor).unwrap(),
+            LinuxSllHeader {
+                packet_type: LinuxSllPacketType::OUTGOING,
+                arp_hrd_type: ArpHardwareId::ETHER,
+                sender_address_valid_length: 6,
+                sender_address: [7, 8, 9, 10, 11, 12, 0, 0],
+                protocol_type: LinuxSllProtocolType::EtherType(EtherType::IPV6)
+            }
+        );
+
+        //ip header
+        let ip_actual = Ipv6Header::read(&mut cursor).unwrap();
+        let ip_expected = Ipv6Header {
+            traffic_class: 1,
+            flow_label: 2.try_into().unwrap(),
+            payload_length: (UdpHeader::LEN + in_payload.len()) as u16,
+            next_header: ip_number::UDP,
+            hop_limit: 47,
+            source: [
+                11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26,
+            ],
+            destination: [
+                31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+            ],
+        };
+        assert_eq!(ip_actual, ip_expected);
+
+        //udp header
+        let udp_actual = UdpHeader::read(&mut cursor).unwrap();
+        let udp_expected =
+            UdpHeader::with_ipv6_checksum(48, 49, &ip_expected, &in_payload).unwrap();
+        assert_eq!(udp_actual, udp_expected);
+
+        //payload
+        let mut actual_payload: [u8; 4] = [0; 4];
+        cursor.read_exact(&mut actual_payload).unwrap();
+        assert_eq!(actual_payload, in_payload);
+    }
+
+    #[test]
+    fn udp_builder_eth_vlan_ip_udp() {
+        //generate
+        let in_payload = [50, 51, 52, 53];
+        let mut serialized = Vec::new();
+        PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12])
+            .vlan(VlanHeader::Single(SingleVlanHeader {
+                pcp: 1.try_into().unwrap(),
+                drop_eligible_indicator: true,
+                vlan_id: 0x123.try_into().unwrap(),
+                ether_type: 0.into(), //should be overwritten
+            }))
+            .ip(IpHeaders::Ipv6(
+                Ipv6Header {
+                    traffic_class: 1,
+                    flow_label: 2.try_into().unwrap(),
+                    payload_length: (UdpHeader::LEN + in_payload.len()) as u16,
+                    next_header: ip_number::UDP,
+                    hop_limit: 47,
+                    source: [
+                        11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26,
+                    ],
+                    destination: [
+                        31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+                    ],
+                },
+                Default::default(),
+            ))
+            .udp(48, 49)
+            .write(&mut serialized, &in_payload)
+            .unwrap();
+
+        //check the deserialized size
+        assert_eq!(
+            Ethernet2Header::LEN
+                + SingleVlanHeader::LEN
+                + Ipv6Header::LEN
+                + UdpHeader::LEN
+                + in_payload.len(),
+            serialized.len()
+        );
+
+        //deserialize and check that everything is as expected
+        use std::io::Cursor;
+        use std::io::Read;
+
+        //deserialize each part of the message and check it
+        let mut cursor = Cursor::new(&serialized);
+
+        //ethernet 2 header
+        assert_eq!(
+            Ethernet2Header::read(&mut cursor).unwrap(),
+            Ethernet2Header {
+                source: [1, 2, 3, 4, 5, 6],
+                destination: [7, 8, 9, 10, 11, 12],
+                ether_type: ether_type::VLAN_TAGGED_FRAME
+            }
+        );
+
+        //outer vlan header
+        assert_eq!(
+            SingleVlanHeader::read(&mut cursor).unwrap(),
+            SingleVlanHeader {
+                pcp: 1.try_into().unwrap(),
+                drop_eligible_indicator: true,
+                vlan_id: 0x123.try_into().unwrap(),
+                ether_type: ether_type::IPV6
+            }
+        );
+
+        //ip header
+        let ip_actual = Ipv6Header::read(&mut cursor).unwrap();
+        let ip_expected = Ipv6Header {
+            traffic_class: 1,
+            flow_label: 2.try_into().unwrap(),
+            payload_length: (UdpHeader::LEN + in_payload.len()) as u16,
+            next_header: ip_number::UDP,
+            hop_limit: 47,
+            source: [
+                11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26,
+            ],
+            destination: [
+                31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+            ],
+        };
+        assert_eq!(ip_actual, ip_expected);
+
+        //udp header
+        let udp_actual = UdpHeader::read(&mut cursor).unwrap();
+        let udp_expected =
+            UdpHeader::with_ipv6_checksum(48, 49, &ip_expected, &in_payload).unwrap();
+        assert_eq!(udp_actual, udp_expected);
+
+        //payload
+        let mut actual_payload: [u8; 4] = [0; 4];
+        cursor.read_exact(&mut actual_payload).unwrap();
+        assert_eq!(actual_payload, in_payload);
+    }
+
+    proptest! {
+        #[test]
+        fn tcp_ipv4(ref input in tcp_any()) {
+
+            //payload
+            let in_payload = [24,25,26,27];
+
+            //ip v4 header
+            let mut ip_expected = Ipv4Header::new(
+                in_payload.len() as u16 + input.header_len_u16(),
+                21, //ttl
+                ip_number::TCP,
+                [13,14,15,16],
+                [17,18,19,20]
+            ).unwrap();
+            ip_expected.header_checksum = ip_expected.calc_header_checksum();
+
+            //generated the expected output
+            let expected = {
+                let mut expected = input.clone();
+                //replace urg & ack if the flags are not set
+                if !expected.ack {
+                    expected.acknowledgment_number = 0;
+                }
+                if !expected.urg {
+                    expected.urgent_pointer = 0;
+                }
+                //calculate the checksum
+                expected.checksum = expected.calc_checksum_ipv4(&ip_expected, &in_payload[..]).unwrap();
+                //done
+                expected
+            };
+
+            //generate
+            let serialized = {
+
+                //create builder
+                let mut builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                                                .ipv4([13,14,15,16], [17,18,19,20], 21)
+                                                .tcp(input.source_port,
+                                                    input.destination_port,
+                                                    input.sequence_number,
+                                                    input.window_size)
+                                                .options_raw(input.options.as_slice()).unwrap();
+                //set the flags
+                if input.ns {
+                    builder = builder.ns();
+                }
+                if input.fin {
+                    builder = builder.fin();
+                }
+                if input.syn {
+                    builder = builder.syn();
+                }
+                if input.rst {
+                    builder = builder.rst();
+                }
+                if input.psh {
+                    builder = builder.psh();
+                }
+                if input.ack {
+                    builder = builder.ack(input.acknowledgment_number);
+                }
+                if input.urg {
+                    builder = builder.urg(input.urgent_pointer);
+                }
+                if input.ece {
+                    builder = builder.ece();
+                }
+                if input.cwr {
+                    builder = builder.cwr();
+                }
+
+                let mut serialized = Vec::new();
+                builder.write(&mut serialized, &in_payload).unwrap();
+                serialized
+            };
+
+            //deserialize and check that everything is as expected
+            use std::io::Cursor;
+            use std::io::Read;
+            //deserialize each part of the message and check it
+            let mut cursor = Cursor::new(&serialized);
+
+            //ethernet 2 header
+            assert_eq!(Ethernet2Header::read(&mut cursor).unwrap(),
+                    Ethernet2Header{
+                            source: [1,2,3,4,5,6],
+                            destination: [7,8,9,10,11,12],
+                            ether_type: ether_type::IPV4
+                    });
+
+            //ip header
+            let ip_actual = Ipv4Header::read(&mut cursor).unwrap();
+            assert_eq!(ip_actual,
+                    ip_expected);
+
+            //tcp header
+            assert_eq!(TcpHeader::read(&mut cursor).unwrap(),
+                    expected);
+
+            //payload
+            let mut actual_payload: [u8;4] = [0;4];
+            cursor.read_exact(&mut actual_payload).unwrap();
+            assert_eq!(actual_payload, in_payload);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn tcp_ipv6(ref input in tcp_any()) {
+
+            //payload
+            let in_payload = [24,25,26,27];
+
+            //ip v4 header
+            let ip_expected = Ipv6Header{
+                traffic_class: 0,
+                flow_label: Ipv6FlowLabel::ZERO,
+                payload_length: (input.header_len() as usize + in_payload.len()) as u16,
+                next_header: ip_number::TCP,
+                hop_limit: 47,
+                source: [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26],
+                destination: [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46]
+            };
+
+            //generated the expected output
+            let expected = {
+                let mut expected = input.clone();
+                //replace urg & ack if the flags are not set
+                if !expected.ack {
+                    expected.acknowledgment_number = 0;
+                }
+                if !expected.urg {
+                    expected.urgent_pointer = 0;
+                }
+                //calculate the checksum
+                expected.checksum = expected.calc_checksum_ipv6(&ip_expected, &in_payload[..]).unwrap();
+                //done
+                expected
+            };
+
+            //generate
+            let serialized = {
+
+                //create builder
+                let mut builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                                                .ipv6([11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26],
+                                                    [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46],
+                                                    47,
+                                                )
+                                                .tcp(input.source_port,
+                                                    input.destination_port,
+                                                    input.sequence_number,
+                                                    input.window_size)
+                                                .options_raw(input.options.as_slice()).unwrap();
+                //set the flags
+                if input.ns {
+                    builder = builder.ns();
+                }
+                if input.fin {
+                    builder = builder.fin();
+                }
+                if input.syn {
+                    builder = builder.syn();
+                }
+                if input.rst {
+                    builder = builder.rst();
+                }
+                if input.psh {
+                    builder = builder.psh();
+                }
+                if input.ack {
+                    builder = builder.ack(input.acknowledgment_number);
+                }
+                if input.urg {
+                    builder = builder.urg(input.urgent_pointer);
+                }
+                if input.ece {
+                    builder = builder.ece();
+                }
+                if input.cwr {
+                    builder = builder.cwr();
+                }
+
+                let mut serialized = Vec::new();
+                builder.write(&mut serialized, &in_payload).unwrap();
+                serialized
+            };
+
+            //deserialize and check that everything is as expected
+            use std::io::Cursor;
+            use std::io::Read;
+            //deserialize each part of the message and check it
+            let mut cursor = Cursor::new(&serialized);
+
+            //ethernet 2 header
+            assert_eq!(Ethernet2Header::read(&mut cursor).unwrap(),
+                    Ethernet2Header{
+                            source: [1,2,3,4,5,6],
+                            destination: [7,8,9,10,11,12],
+                            ether_type: ether_type::IPV6
+                    });
+
+            //ip header
+            let ip_actual = Ipv6Header::read(&mut cursor).unwrap();
+            assert_eq!(ip_actual,
+                    ip_expected);
+
+            //tcp header
+            assert_eq!(TcpHeader::read(&mut cursor).unwrap(),
+                    expected);
+
+            //payload
+            let mut actual_payload: [u8;4] = [0;4];
+            cursor.read_exact(&mut actual_payload).unwrap();
+            assert_eq!(actual_payload, in_payload);
+        }
+    }
+
+    #[test]
+    fn tcp_options() {
+        let mut serialized = Vec::new();
+
+        use crate::TcpOptionElement::*;
+        let options = vec![MaximumSegmentSize(1234), Noop];
+
+        PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12])
+            .ipv4([13, 14, 15, 16], [17, 18, 19, 20], 21)
+            .tcp(1, 2, 3, 4)
+            .options(&options)
+            .unwrap()
+            .write(&mut serialized, &[])
+            .unwrap();
+
+        let decoded = PacketHeaders::from_ethernet_slice(&serialized[..]).unwrap();
+        let dec_options: Vec<Result<TcpOptionElement, TcpOptionReadError>> = decoded
+            .transport
+            .unwrap()
+            .tcp()
+            .unwrap()
+            .options_iterator()
+            .collect();
+        assert_eq!(&[Ok(MaximumSegmentSize(1234)), Ok(Noop)], &dec_options[..]);
+    }
+
+    #[test]
+    fn size() {
+        //ipv4 no vlan ethernet
+        assert_eq!(
+            Ethernet2Header::LEN + Ipv4Header::MIN_LEN + UdpHeader::LEN + 123,
+            PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12])
+                .ipv4([13, 14, 15, 16], [17, 18, 19, 20], 21)
+                .udp(22, 23)
+                .size(123)
+        );
+
+        //ipv6 no vlan ethernet
+        assert_eq!(
+            Ethernet2Header::LEN + Ipv6Header::LEN + UdpHeader::LEN + 123,
+            PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12])
+                .ipv6(
+                    [11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26],
+                    [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46],
+                    47,
+                )
+                .udp(22, 23)
+                .size(123)
+        );
+
+        //ipv4 linux_sll
+        assert_eq!(
+            LinuxSllHeader::LEN + Ipv4Header::MIN_LEN + UdpHeader::LEN + 123,
+            PacketBuilder::linux_sll(LinuxSllPacketType::OUTGOING, 6, [7, 8, 9, 10, 11, 12, 0, 0])
+                .ipv4([13, 14, 15, 16], [17, 18, 19, 20], 21)
+                .udp(22, 23)
+                .size(123)
+        );
+
+        //ipv6 linux_sll
+        assert_eq!(
+            LinuxSllHeader::LEN + Ipv6Header::LEN + UdpHeader::LEN + 123,
+            PacketBuilder::linux_sll(LinuxSllPacketType::OUTGOING, 6, [7, 8, 9, 10, 11, 12, 0, 0])
+                .ipv6(
+                    [11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26],
+                    [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46],
+                    47,
+                )
+                .udp(22, 23)
+                .size(123)
+        );
+
+        //ipv4 single vlan ethernet
+        assert_eq!(
+            Ethernet2Header::LEN
+                + SingleVlanHeader::LEN
+                + Ipv4Header::MIN_LEN
+                + UdpHeader::LEN
+                + 123,
+            PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12])
+                .single_vlan(0x123.try_into().unwrap())
+                .ipv4([13, 14, 15, 16], [17, 18, 19, 20], 21)
+                .udp(22, 23)
+                .size(123)
+        );
+
+        //ipv6 double vlan ethernet
+        assert_eq!(
+            Ethernet2Header::LEN + DoubleVlanHeader::LEN + 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(
+                    [11, 12, 13, 14, 15, 16, 17, 18, 19, 10, 21, 22, 23, 24, 25, 26],
+                    [31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46],
+                    47,
+                )
+                .udp(22, 23)
+                .size(123)
+        );
+    }
+
+    proptest! {
+        #[test]
+        fn size_tcp(ref input in tcp_any()) {
+
+            assert_eq!(Ethernet2Header::LEN +
+                    Ipv4Header::MIN_LEN +
+                    input.header_len() as usize +
+                    123,
+
+                    PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                                    .ipv4([13,14,15,16], [17,18,19,20], 21)
+                                    .tcp(input.source_port,
+                                        input.destination_port,
+                                        input.sequence_number,
+                                        input.window_size)
+                                    .options_raw(input.options.as_slice()).unwrap()
+                                    .size(123));
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn ipv4_icmpv4(
+            ipv4_source in any::<[u8;4]>(),
+            ipv4_dest in any::<[u8;4]>(),
+            ipv4_time_to_live in any::<u8>(),
+            icmpv4_type_u8 in 15u8..u8::MAX,
+            icmpv4_code_u8 in any::<u8>(),
+            icmpv4_bytes5to8 in any::<[u8;4]>(),
+            icmpv4 in icmpv4_type_any(),
+            echo_id in any::<u16>(),
+            echo_seq in any::<u16>(),
+            payload in proptest::collection::vec(any::<u8>(), 0..64),
+        ) {
+            let test_builder = |builder: PacketBuilderStep<Icmpv4Header>, icmpv4_type: Icmpv4Type| {
+                use crate::Icmpv4Type::*;
+                let adapted_payload = match &icmpv4_type {
+                    TimestampRequest(_) |
+                    TimestampReply(_) => &[],
+                    _ => &payload[..],
+                };
+                let icmp_expected = Icmpv4Header::with_checksum(icmpv4_type, &adapted_payload);
+                let ip_expected = {
+                    let mut expected_ipv4 = Ipv4Header::new(
+                        (icmp_expected.header_len() + adapted_payload.len()) as u16,
+                        ipv4_time_to_live,
+                        ip_number::ICMP,
+                        ipv4_source,
+                        ipv4_dest
+                    ).unwrap();
+                    expected_ipv4.header_checksum = expected_ipv4.calc_header_checksum();
+                    expected_ipv4
+                };
+
+                // test builder.size()
+                assert_eq!(
+                    builder.size(adapted_payload.len()),
+                    Ethernet2Header::LEN +
+                    Ipv4Header::MIN_LEN +
+                    icmp_expected.header_len() +
+                    adapted_payload.len()
+                );
+
+                // test builder.write()
+                let mut buffer = Vec::<u8>::with_capacity(builder.size(adapted_payload.len()));
+                builder.write(&mut buffer, adapted_payload).unwrap();
+
+                // decode packets
+                let actual = PacketHeaders::from_ethernet_slice(&buffer).unwrap();
+
+                // check the packets could be decoded
+                assert_eq!(
+                    Some(LinkHeader::Ethernet2(Ethernet2Header{
+                        source: [1,2,3,4,5,6],
+                        destination: [7,8,9,10,11,12],
+                        ether_type: ether_type::IPV4
+                    })),
+                    actual.link
+                );
+                assert_eq!(
+                    Some(NetHeaders::Ipv4(ip_expected, Default::default())),
+                    actual.net
+                );
+                assert_eq!(
+                    Some(TransportHeader::Icmpv4(icmp_expected)),
+                    actual.transport
+                );
+                assert_eq!(actual.payload.slice(), adapted_payload);
+            };
+
+            // icmpv4
+            {
+                let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                    .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live)
+                    .icmpv4(icmpv4.clone());
+
+                test_builder(
+                    builder,
+                    icmpv4
+                );
+            }
+
+            // icmpv4_raw
+            {
+                let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                    .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live)
+                    .icmpv4_raw(icmpv4_type_u8, icmpv4_code_u8, icmpv4_bytes5to8);
+
+                test_builder(
+                    builder,
+                    Icmpv4Type::Unknown{
+                        type_u8: icmpv4_type_u8,
+                        code_u8: icmpv4_code_u8,
+                        bytes5to8: icmpv4_bytes5to8,
+                    }
+                );
+            }
+
+            // icmpv4_echo_request
+            {
+                let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                    .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live)
+                    .icmpv4_echo_request(echo_id, echo_seq);
+
+                test_builder(
+                    builder,
+                    Icmpv4Type::EchoRequest(IcmpEchoHeader{
+                        id: echo_id,
+                        seq: echo_seq,
+                    })
+                );
+            }
+
+            // icmp4_echo_reply
+            {
+                let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                    .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live)
+                    .icmpv4_echo_reply(echo_id, echo_seq);
+
+                test_builder(
+                    builder,
+                    Icmpv4Type::EchoReply(IcmpEchoHeader{
+                        id: echo_id,
+                        seq: echo_seq,
+                    })
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn ipv4_icmpv6(
+            ipv4_source in any::<[u8;4]>(),
+            ipv4_dest in any::<[u8;4]>(),
+            ipv4_time_to_live in any::<u8>(),
+            icmpv6_type_u8 in 162u8..u8::MAX,
+            icmpv6_code_u8 in any::<u8>(),
+            icmpv6_bytes5to8 in any::<[u8;4]>(),
+            icmpv6 in icmpv6_type_any(),
+            echo_id in any::<u16>(),
+            echo_seq in any::<u16>(),
+            payload in proptest::collection::vec(any::<u8>(), 0..64),
+        ) {
+            let test_builder = |builder: PacketBuilderStep<Icmpv6Header>, icmpv6_type: Icmpv6Type| {
+                // test builder.size()
+                assert_eq!(
+                    builder.size(payload.len()),
+                    Ethernet2Header::LEN +
+                    Ipv4Header::MIN_LEN +
+                    icmpv6_type.header_len() +
+                    payload.len()
+                );
+
+                // test builder.write()
+                let mut buffer = Vec::<u8>::with_capacity(builder.size(payload.len()));
+                // should trigger an error, was it is not possible to calculate the checksum
+                assert!(builder.write(&mut buffer, &payload).is_err());
+            };
+
+            // icmpv6
+            {
+                let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                    .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live)
+                    .icmpv6(icmpv6.clone());
+
+                test_builder(
+                    builder,
+                    icmpv6
+                );
+            }
+
+            // icmpv6_raw
+            {
+                let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                    .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live)
+                    .icmpv6_raw(icmpv6_type_u8, icmpv6_code_u8, icmpv6_bytes5to8);
+
+                test_builder(
+                    builder,
+                    Icmpv6Type::Unknown{
+                        type_u8: icmpv6_type_u8,
+                        code_u8: icmpv6_code_u8,
+                        bytes5to8: icmpv6_bytes5to8,
+                    }
+                );
+            }
+
+            // icmpv6_echo_request
+            {
+                let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                    .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live)
+                    .icmpv6_echo_request(echo_id, echo_seq);
+
+                test_builder(
+                    builder,
+                    Icmpv6Type::EchoRequest(IcmpEchoHeader{
+                        id: echo_id,
+                        seq: echo_seq,
+                    })
+                );
+            }
+
+            // icmp4_echo_reply
+            {
+                let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                    .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live)
+                    .icmpv6_echo_reply(echo_id, echo_seq);
+
+                test_builder(
+                    builder,
+                    Icmpv6Type::EchoReply(IcmpEchoHeader{
+                        id: echo_id,
+                        seq: echo_seq,
+                    })
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn ipv6_icmpv4(
+            ipv6_source in any::<[u8;16]>(),
+            ipv6_dest in any::<[u8;16]>(),
+            ipv6_hop_limit in any::<u8>(),
+            icmpv4_type_u8 in 15u8..u8::MAX,
+            icmpv4_code_u8 in any::<u8>(),
+            icmpv4_bytes5to8 in any::<[u8;4]>(),
+            icmpv4 in icmpv4_type_any(),
+            echo_id in any::<u16>(),
+            echo_seq in any::<u16>(),
+            payload in proptest::collection::vec(any::<u8>(), 0..64),
+        ) {
+            let test_builder = |builder: PacketBuilderStep<Icmpv4Header>, icmpv4_type: Icmpv4Type| {
+
+                use Icmpv4Type::*;
+                let adapted_payload = match icmpv4_type {
+                    TimestampRequest(_) | TimestampReply(_) => &[],
+                    _ => &payload[..],
+                };
+
+                let icmp_expected = Icmpv4Header::with_checksum(icmpv4_type, &adapted_payload);
+                let ip_expected = Ipv6Header{
+                    traffic_class: 0,
+                    flow_label: Ipv6FlowLabel::ZERO,
+                    payload_length: (icmp_expected.header_len() + adapted_payload.len()) as u16,
+                    next_header: ip_number::ICMP,
+                    hop_limit: ipv6_hop_limit,
+                    source: ipv6_source,
+                    destination: ipv6_dest
+                };
+
+                // test builder.size()
+                assert_eq!(
+                    builder.size(adapted_payload.len()),
+                    Ethernet2Header::LEN +
+                    Ipv6Header::LEN +
+                    icmp_expected.header_len() +
+                    adapted_payload.len()
+                );
+
+                // test builder.write()
+                let mut buffer = Vec::<u8>::with_capacity(builder.size(adapted_payload.len()));
+                builder.write(&mut buffer, adapted_payload).unwrap();
+
+                // decode packets
+                let actual = PacketHeaders::from_ethernet_slice(&buffer).unwrap();
+
+                // check the packets could be decoded
+                assert_eq!(
+                    Some(LinkHeader::Ethernet2(Ethernet2Header{
+                        source: [1,2,3,4,5,6],
+                        destination: [7,8,9,10,11,12],
+                        ether_type: ether_type::IPV6
+                    })),
+                    actual.link
+                );
+                assert_eq!(
+                    Some(NetHeaders::Ipv6(ip_expected, Default::default())),
+                    actual.net
+                );
+                assert_eq!(
+                    Some(TransportHeader::Icmpv4(icmp_expected)),
+                    actual.transport
+                );
+                assert_eq!(actual.payload.slice(), adapted_payload);
+            };
+
+            // icmpv4
+            {
+                let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                    .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit)
+                    .icmpv4(icmpv4.clone());
+
+                test_builder(
+                    builder,
+                    icmpv4
+                );
+            }
+
+            // icmpv4_raw
+            {
+                let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                    .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit)
+                    .icmpv4_raw(icmpv4_type_u8, icmpv4_code_u8, icmpv4_bytes5to8);
+
+                test_builder(
+                    builder,
+                    Icmpv4Type::Unknown{
+                        type_u8: icmpv4_type_u8,
+                        code_u8: icmpv4_code_u8,
+                        bytes5to8: icmpv4_bytes5to8,
+                    }
+                );
+            }
+
+            // icmpv4_echo_request
+            {
+                let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                    .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit)
+                    .icmpv4_echo_request(echo_id, echo_seq);
+
+                test_builder(
+                    builder,
+                    Icmpv4Type::EchoRequest(IcmpEchoHeader{
+                        id: echo_id,
+                        seq: echo_seq,
+                    })
+                );
+            }
+
+            // icmp4_echo_reply
+            {
+                let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                    .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit)
+                    .icmpv4_echo_reply(echo_id, echo_seq);
+
+                test_builder(
+                    builder,
+                    Icmpv4Type::EchoReply(IcmpEchoHeader{
+                        id: echo_id,
+                        seq: echo_seq,
+                    })
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn ipv6_icmpv6(
+            ipv6_source in any::<[u8;16]>(),
+            ipv6_dest in any::<[u8;16]>(),
+            ipv6_hop_limit in any::<u8>(),
+            icmpv6_type_u8 in 162u8..u8::MAX,
+            icmpv6_code_u8 in any::<u8>(),
+            icmpv6_bytes5to8 in any::<[u8;4]>(),
+            icmpv6 in icmpv6_type_any(),
+            echo_id in any::<u16>(),
+            echo_seq in any::<u16>(),
+            payload in proptest::collection::vec(any::<u8>(), 0..64),
+        ) {
+            let test_builder = |builder: PacketBuilderStep<Icmpv6Header>, icmpv6_type: Icmpv6Type| {
+                let icmp_expected = Icmpv6Header::with_checksum(
+                    icmpv6_type,
+                    ipv6_source,
+                    ipv6_dest,
+                    &payload
+                ).unwrap();
+                let ip_expected = Ipv6Header{
+                    traffic_class: 0,
+                    flow_label: Ipv6FlowLabel::ZERO,
+                    payload_length: (icmp_expected.header_len() + payload.len()) as u16,
+                    next_header: ip_number::IPV6_ICMP,
+                    hop_limit: ipv6_hop_limit,
+                    source: ipv6_source,
+                    destination: ipv6_dest
+                };
+
+                // test builder.size()
+                assert_eq!(
+                    builder.size(payload.len()),
+                    Ethernet2Header::LEN +
+                    Ipv6Header::LEN +
+                    icmp_expected.header_len() +
+                    payload.len()
+                );
+
+                // test builder.write()
+                let mut buffer = Vec::<u8>::with_capacity(builder.size(payload.len()));
+                builder.write(&mut buffer, &payload).unwrap();
+
+                // decode packets
+                let actual = PacketHeaders::from_ethernet_slice(&buffer).unwrap();
+
+                // check the packets could be decoded
+                assert_eq!(
+                    Some(LinkHeader::Ethernet2(Ethernet2Header{
+                        source: [1,2,3,4,5,6],
+                        destination: [7,8,9,10,11,12],
+                        ether_type: ether_type::IPV6
+                    })),
+                    actual.link
+                );
+                assert_eq!(
+                    Some(NetHeaders::Ipv6(ip_expected, Default::default())),
+                    actual.net
+                );
+                assert_eq!(
+                    Some(TransportHeader::Icmpv6(icmp_expected)),
+                    actual.transport
+                );
+                assert_eq!(actual.payload.slice(), &payload);
+            };
+
+            // icmpv6
+            {
+                let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                    .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit)
+                    .icmpv6(icmpv6.clone());
+
+                test_builder(
+                    builder,
+                    icmpv6
+                );
+            }
+
+            // icmpv6_raw
+            {
+                let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                    .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit)
+                    .icmpv6_raw(icmpv6_type_u8, icmpv6_code_u8, icmpv6_bytes5to8);
+
+                test_builder(
+                    builder,
+                    Icmpv6Type::Unknown{
+                        type_u8: icmpv6_type_u8,
+                        code_u8: icmpv6_code_u8,
+                        bytes5to8: icmpv6_bytes5to8,
+                    }
+                );
+            }
+
+            // icmpv6_echo_request
+            {
+                let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                    .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit)
+                    .icmpv6_echo_request(echo_id, echo_seq);
+
+                test_builder(
+                    builder,
+                    Icmpv6Type::EchoRequest(IcmpEchoHeader{
+                        id: echo_id,
+                        seq: echo_seq,
+                    })
+                );
+            }
+
+            // icmp4_echo_reply
+            {
+                let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12])
+                    .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit)
+                    .icmpv6_echo_reply(echo_id, echo_seq);
+
+                test_builder(
+                    builder,
+                    Icmpv6Type::EchoReply(IcmpEchoHeader{
+                        id: echo_id,
+                        seq: echo_seq,
+                    })
+                );
+            }
+        }
+    }
+}
diff --git a/src/packet_headers.rs b/src/packet_headers.rs
new file mode 100644
index 0000000..a14f805
--- /dev/null
+++ b/src/packet_headers.rs
@@ -0,0 +1,1256 @@
+use crate::err::LenError;
+
+use super::*;
+
+/// Decoded packet headers (data link layer and lower).
+///
+/// You can use
+///
+/// * [`PacketHeaders::from_ethernet_slice`]
+/// * [`PacketHeaders::from_ether_type`]
+/// * [`PacketHeaders::from_ip_slice`]
+///
+/// depending on your starting header to parse the headers in a slice and get this
+/// struct as a result.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct PacketHeaders<'a> {
+    /// Ethernet II header if present.
+    pub link: Option<LinkHeader>,
+    /// Single or double vlan headers if present.
+    pub vlan: Option<VlanHeader>,
+    /// IPv4 or IPv6 header and IP extension headers if present.
+    pub net: Option<NetHeaders>,
+    /// TCP or UDP header if present.
+    pub transport: Option<TransportHeader>,
+    /// Payload of the last parsed layer.
+    pub payload: PayloadSlice<'a>,
+}
+
+impl<'a> PacketHeaders<'a> {
+    /// Decodes a network packet into different headers from a slice that starts with an Ethernet II header.
+    ///
+    /// The result is returned as a [`PacketHeaders`] struct.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    ///```
+    /// # use etherparse::{Ethernet2Header, PacketBuilder};
+    /// # let builder = PacketBuilder::
+    /// #    ethernet2([1,2,3,4,5,6],     //source mac
+    /// #               [7,8,9,10,11,12]) //destination mac
+    /// #    .ipv4([192,168,1,1], //source ip
+    /// #          [192,168,1,2], //destination ip
+    /// #          20)            //time to life
+    /// #    .udp(21,    //source port
+    /// #         1234); // destination port
+    /// # // payload of the udp packet
+    /// # let payload = [1,2,3,4,5,6,7,8];
+    /// # // get some memory to store the serialized data
+    /// # let mut complete_packet = Vec::<u8>::with_capacity(
+    /// #     builder.size(payload.len())
+    /// # );
+    /// # builder.write(&mut complete_packet, &payload).unwrap();
+    /// #
+    /// # // skip ethernet 2 header so we can parse from there downwards
+    /// # let packet = &complete_packet[Ethernet2Header::LEN..];
+    /// #
+    /// use etherparse::{ether_type, PacketHeaders};
+    ///
+    /// match PacketHeaders::from_ether_type(ether_type::IPV4, packet) {
+    ///     Err(value) => println!("Err {:?}", value),
+    ///     Ok(value) => {
+    ///         println!("link: {:?}", value.link);
+    ///         println!("vlan: {:?}", value.vlan);
+    ///         println!("net: {:?}", value.net);
+    ///         println!("transport: {:?}", value.transport);
+    ///     }
+    /// }
+    /// ```
+    pub fn from_ethernet_slice(
+        slice: &'a [u8],
+    ) -> Result<PacketHeaders<'a>, err::packet::SliceError> {
+        use err::packet::SliceError::Len;
+
+        let (ethernet, rest) = Ethernet2Header::from_slice(slice).map_err(Len)?;
+        let mut result = Self::from_ether_type(ethernet.ether_type, rest);
+
+        match &mut result {
+            // inject ethernet header into the result
+            Ok(result) => result.link = Some(LinkHeader::Ethernet2(ethernet)),
+            // add the ethernet header to the overall offset in case there is a length error
+            Err(Len(err)) => err.layer_start_offset += Ethernet2Header::LEN,
+            _ => {}
+        }
+        result
+    }
+
+    /// Tries to decode a network packet into different headers using the
+    /// given `ether_type` number to identify the first header.
+    ///
+    /// The result is returned as a [`PacketHeaders`] struct. Currently supported
+    /// ether type numbers are:
+    ///
+    /// * `ether_type::IPV4`
+    /// * `ether_type::IPV6`
+    /// * `ether_type::VLAN_TAGGED_FRAME`
+    /// * `ether_type::PROVIDER_BRIDGING`
+    /// * `ether_type::VLAN_DOUBLE_TAGGED_FRAME`
+    ///
+    /// If an unsupported ether type is given the given slice will be set as payload
+    /// and all other fields will be set to `None`.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    ///```
+    /// # use etherparse::{Ethernet2Header, PacketBuilder};
+    /// # let builder = PacketBuilder::
+    /// #    ethernet2([1,2,3,4,5,6],     //source mac
+    /// #               [7,8,9,10,11,12]) //destination mac
+    /// #    .ipv4([192,168,1,1], //source ip
+    /// #          [192,168,1,2], //destination ip
+    /// #          20)            //time to life
+    /// #    .udp(21,    //source port
+    /// #         1234); // destination port
+    /// # // payload of the udp packet
+    /// # let payload = [1,2,3,4,5,6,7,8];
+    /// # // get some memory to store the serialized data
+    /// # let mut complete_packet = Vec::<u8>::with_capacity(
+    /// #     builder.size(payload.len())
+    /// # );
+    /// # builder.write(&mut complete_packet, &payload).unwrap();
+    /// #
+    /// # // skip ethernet 2 header so we can parse from there downwards
+    /// # let packet = &complete_packet[Ethernet2Header::LEN..];
+    /// #
+    /// use etherparse::{ether_type, PacketHeaders};
+    ///
+    /// match PacketHeaders::from_ether_type(ether_type::IPV4, packet) {
+    ///     Err(value) => println!("Err {:?}", value),
+    ///     Ok(value) => {
+    ///         println!("link: {:?}", value.link);
+    ///         println!("vlan: {:?}", value.vlan);
+    ///         println!("net: {:?}", value.net);
+    ///         println!("transport: {:?}", value.transport);
+    ///     }
+    /// }
+    /// ```
+    pub fn from_ether_type(
+        mut ether_type: EtherType,
+        slice: &'a [u8],
+    ) -> Result<PacketHeaders<'a>, err::packet::SliceError> {
+        use err::packet::SliceError::*;
+
+        let mut rest = slice;
+
+        // helper function to add the current offset to length errors
+        let add_offset = |mut len_error: LenError, rest: &[u8]| -> LenError {
+            len_error.layer_start_offset += unsafe {
+                // SAFETY: Safe as rest is a subslice of slice.
+                rest.as_ptr().offset_from(slice.as_ptr()) as usize
+            };
+            len_error
+        };
+
+        let mut result = PacketHeaders {
+            link: None,
+            vlan: None,
+            net: None,
+            transport: None,
+            payload: PayloadSlice::Ether(EtherPayloadSlice {
+                ether_type,
+                payload: rest,
+            }),
+        };
+
+        //parse vlan header(s)
+        use ether_type::*;
+
+        result.vlan = match ether_type {
+            VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => {
+                use crate::VlanHeader::*;
+                let (outer, outer_rest) = SingleVlanHeader::from_slice(rest).map_err(Len)?;
+
+                //set the rest & ether_type for the following operations
+                rest = outer_rest;
+                ether_type = outer.ether_type;
+                result.payload = PayloadSlice::Ether(EtherPayloadSlice {
+                    ether_type,
+                    payload: rest,
+                });
+
+                //parse second vlan header if present
+                match ether_type {
+                    //second vlan tagging header
+                    VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => {
+                        let (inner, inner_rest) = SingleVlanHeader::from_slice(rest)
+                            .map_err(|err| Len(err.add_offset(SingleVlanHeader::LEN)))?;
+
+                        //set the rest & ether_type for the following operations
+                        rest = inner_rest;
+                        ether_type = inner.ether_type;
+                        result.payload = PayloadSlice::Ether(EtherPayloadSlice {
+                            ether_type,
+                            payload: rest,
+                        });
+
+                        Some(Double(DoubleVlanHeader { outer, inner }))
+                    }
+                    //no second vlan header detected -> single vlan header
+                    _ => Some(Single(outer)),
+                }
+            }
+            //no vlan header
+            _ => None,
+        };
+
+        // parse ip
+        match ether_type {
+            IPV4 => {
+                // read ipv4 header & extensions and payload slice
+                let (ip, ip_payload) = IpHeaders::from_ipv4_slice(rest).map_err(|err| {
+                    use err::ipv4::SliceError as I;
+                    match err {
+                        I::Len(err) => Len(add_offset(err, rest)),
+                        I::Header(err) => Ipv4(err),
+                        I::Exts(err) => Ipv4Exts(err),
+                    }
+                })?;
+
+                // set the next
+                rest = ip_payload.payload;
+                result.net = Some(ip.into());
+                result.payload = PayloadSlice::Ip(ip_payload.clone());
+
+                // decode transport layer
+                let (transport, payload) = read_transport(ip_payload).map_err(|err| {
+                    use err::tcp::HeaderSliceError as I;
+                    match err {
+                        I::Len(err) => Len(add_offset(err, rest)),
+                        I::Content(err) => Tcp(err),
+                    }
+                })?;
+
+                result.transport = transport;
+                result.payload = payload;
+            }
+            IPV6 => {
+                // read ipv6 header & extensions and payload slice
+                let (ip, ip_payload) = IpHeaders::from_ipv6_slice(rest).map_err(|err| {
+                    use err::ipv6::SliceError as I;
+                    match err {
+                        I::Len(err) => Len(add_offset(err, rest)),
+                        I::Header(err) => Ipv6(err),
+                        I::Exts(err) => Ipv6Exts(err),
+                    }
+                })?;
+
+                //set the ip result & rest
+                rest = ip_payload.payload;
+                result.net = Some(ip.into());
+                result.payload = PayloadSlice::Ip(ip_payload.clone());
+
+                // decode transport layer
+                let (transport, payload) = read_transport(ip_payload).map_err(|err| {
+                    use err::tcp::HeaderSliceError as I;
+                    match err {
+                        I::Len(err) => Len(add_offset(err, rest)),
+                        I::Content(err) => Tcp(err),
+                    }
+                })?;
+
+                result.transport = transport;
+                result.payload = payload;
+            }
+            _ => {}
+        };
+
+        Ok(result)
+    }
+
+    /// Tries to decode an ip packet and its transport headers.
+    ///
+    /// Assumes the given slice starts with the first byte of the IP header.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// # use etherparse::PacketBuilder;
+    /// # // build a UDP packet
+    /// # let payload = [0u8;18];
+    /// # let builder = PacketBuilder::
+    /// #    ipv4([192,168,1,1], //source ip
+    /// #         [192,168,1,2], //destination ip
+    /// #         20)            //time to life
+    /// #    .udp(21,    //source port
+    /// #        1234); //  destination port
+    /// #
+    /// # // serialize the packet
+    /// # let packet = {
+    /// #     let mut packet = Vec::<u8>::with_capacity(
+    /// #         builder.size(payload.len())
+    /// #     );
+    /// #     builder.write(&mut packet, &payload).unwrap();
+    /// #     packet
+    /// # };
+    /// use etherparse::PacketHeaders;
+    ///
+    /// match PacketHeaders::from_ip_slice(&packet) {
+    ///     Err(value) => println!("Err {:?}", value),
+    ///     Ok(value) => {
+    ///         println!("link: {:?}", value.link);
+    ///         println!("vlan: {:?}", value.vlan);
+    ///         println!("net: {:?}", value.net);
+    ///         println!("transport: {:?}", value.transport);
+    ///     }
+    /// }
+    /// ```
+    pub fn from_ip_slice(slice: &[u8]) -> Result<PacketHeaders, err::packet::SliceError> {
+        use err::packet::SliceError::*;
+
+        // read ip headers
+        let (ip_header, ip_payload) = IpHeaders::from_slice(slice).map_err(|err| {
+            use err::ip::HeadersSliceError as I;
+            match err {
+                I::Len(err) => Len(err),
+                I::Content(err) => match err {
+                    err::ip::HeadersError::Ip(err) => Ip(err),
+                    err::ip::HeadersError::Ipv4Ext(err) => Ipv4Exts(err),
+                    err::ip::HeadersError::Ipv6Ext(err) => Ipv6Exts(err),
+                },
+            }
+        })?;
+
+        let mut result = PacketHeaders {
+            link: None,
+            vlan: None,
+            net: Some(ip_header.into()),
+            transport: None,
+            payload: PayloadSlice::Ip(ip_payload.clone()),
+        };
+
+        // cache rest for offset addition
+        let rest = ip_payload.payload;
+
+        // try to parse the transport header (only if data is not fragmented)
+        let (transport, payload) = read_transport(ip_payload).map_err(|err| {
+            use err::tcp::HeaderSliceError as I;
+            match err {
+                I::Len(mut err) => {
+                    err.layer_start_offset += unsafe {
+                        // SAFETY: Safe as rest is a subslice of slice.
+                        rest.as_ptr().offset_from(slice.as_ptr()) as usize
+                    };
+                    Len(err)
+                }
+                I::Content(err) => Tcp(err),
+            }
+        })?;
+
+        // update output
+        result.transport = transport;
+        result.payload = payload;
+
+        Ok(result)
+    }
+}
+
+/// helper function to process transport headers
+fn read_transport(
+    ip_payload: IpPayloadSlice,
+) -> Result<(Option<TransportHeader>, PayloadSlice), err::tcp::HeaderSliceError> {
+    if ip_payload.fragmented {
+        Ok((None, PayloadSlice::Ip(ip_payload)))
+    } else {
+        // helper function to set the len source in len errors
+        let add_len_source = |mut len_error: LenError| -> err::tcp::HeaderSliceError {
+            // only change the len source if the lower layer has not set it
+            if LenSource::Slice == len_error.len_source {
+                len_error.len_source = ip_payload.len_source;
+            }
+            Len(len_error)
+        };
+        use crate::ip_number::*;
+        use err::tcp::HeaderSliceError::*;
+        match ip_payload.ip_number {
+            ICMP => Icmpv4Slice::from_slice(ip_payload.payload)
+                .map_err(add_len_source)
+                .map(|value| {
+                    (
+                        Some(TransportHeader::Icmpv4(value.header())),
+                        PayloadSlice::Icmpv4(value.payload()),
+                    )
+                }),
+            IPV6_ICMP => Icmpv6Slice::from_slice(ip_payload.payload)
+                .map_err(add_len_source)
+                .map(|value| {
+                    (
+                        Some(TransportHeader::Icmpv6(value.header())),
+                        PayloadSlice::Icmpv6(value.payload()),
+                    )
+                }),
+            UDP => UdpHeader::from_slice(ip_payload.payload)
+                .map_err(add_len_source)
+                .map(|value| {
+                    (
+                        Some(TransportHeader::Udp(value.0)),
+                        PayloadSlice::Udp(value.1),
+                    )
+                }),
+            TCP => TcpHeader::from_slice(ip_payload.payload)
+                .map_err(|err| match err {
+                    Len(err) => add_len_source(err),
+                    Content(err) => Content(err),
+                })
+                .map(|value| {
+                    (
+                        Some(TransportHeader::Tcp(value.0)),
+                        PayloadSlice::Tcp(value.1),
+                    )
+                }),
+            _ => Ok((None, PayloadSlice::Ip(ip_payload))),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::err::packet::SliceError;
+    use crate::test_packet::TestPacket;
+
+    const VLAN_ETHER_TYPES: [EtherType; 3] = [
+        ether_type::VLAN_TAGGED_FRAME,
+        ether_type::PROVIDER_BRIDGING,
+        ether_type::VLAN_DOUBLE_TAGGED_FRAME,
+    ];
+
+    #[test]
+    fn debug() {
+        use alloc::format;
+        let header = PacketHeaders {
+            link: None,
+            vlan: None,
+            net: None,
+            transport: None,
+            payload: PayloadSlice::Ether(EtherPayloadSlice {
+                ether_type: EtherType(0),
+                payload: &[],
+            }),
+        };
+        assert_eq!(
+            &format!("{:?}", header),
+            &format!(
+                "PacketHeaders {{ link: {:?}, vlan: {:?}, net: {:?}, transport: {:?}, payload: {:?} }}",
+                header.link,
+                header.vlan,
+                header.net,
+                header.transport,
+                header.payload
+            )
+        );
+    }
+
+    #[test]
+    fn clone_eq() {
+        let header = PacketHeaders {
+            link: None,
+            vlan: None,
+            net: None,
+            transport: None,
+            payload: PayloadSlice::Ether(EtherPayloadSlice {
+                ether_type: EtherType(0),
+                payload: &[],
+            }),
+        };
+        assert_eq!(header.clone(), header);
+    }
+
+    #[test]
+    fn from_x_slice() {
+        // no eth
+        from_x_slice_vlan_variants(&TestPacket {
+            link: None,
+            vlan: None,
+            net: None,
+            transport: None,
+        });
+
+        // eth
+        {
+            let eth = Ethernet2Header {
+                source: [1, 2, 3, 4, 5, 6],
+                destination: [1, 2, 3, 4, 5, 6],
+                ether_type: 0.into(),
+            };
+            let test = TestPacket {
+                link: Some(LinkHeader::Ethernet2(eth.clone())),
+                vlan: None,
+                net: None,
+                transport: None,
+            };
+
+            // ok ethernet header (with unknown next)
+            from_x_slice_vlan_variants(&test);
+
+            // eth len error
+            {
+                let data = test.to_vec(&[]);
+                for len in 0..data.len() {
+                    let err = LenError {
+                        required_len: eth.header_len(),
+                        len,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::Ethernet2Header,
+                        layer_start_offset: 0,
+                    };
+
+                    from_slice_assert_err(&test, &data[..len], SliceError::Len(err.clone()));
+                }
+            }
+        }
+    }
+
+    fn from_x_slice_vlan_variants(base: &TestPacket) {
+        // none
+        from_x_slice_ip_variants(base);
+
+        // single vlan header
+        {
+            let single = SingleVlanHeader {
+                pcp: 1.try_into().unwrap(),
+                drop_eligible_indicator: false,
+                vlan_id: 2.try_into().unwrap(),
+                ether_type: 3.into(),
+            };
+
+            for vlan_ether_type in VLAN_ETHER_TYPES {
+                let mut test = base.clone();
+                test.set_ether_type(vlan_ether_type);
+                test.vlan = Some(VlanHeader::Single(single.clone()));
+
+                // ok vlan header
+                from_x_slice_ip_variants(&test);
+
+                // len error
+                {
+                    let data = test.to_vec(&[]);
+                    for len in 0..single.header_len() {
+                        let base_len = test.len(&[]) - single.header_len();
+
+                        let err = LenError {
+                            required_len: single.header_len(),
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::VlanHeader,
+                            layer_start_offset: base_len,
+                        };
+                        from_slice_assert_err(
+                            &test,
+                            &data[..base_len + len],
+                            SliceError::Len(err.clone()),
+                        );
+                    }
+                }
+            }
+        }
+
+        // double vlan header
+        for outer_vlan_ether_type in VLAN_ETHER_TYPES {
+            for inner_vlan_ether_type in VLAN_ETHER_TYPES {
+                let double = DoubleVlanHeader {
+                    outer: SingleVlanHeader {
+                        pcp: 1.try_into().unwrap(),
+                        drop_eligible_indicator: false,
+                        vlan_id: 2.try_into().unwrap(),
+                        ether_type: inner_vlan_ether_type,
+                    },
+                    inner: SingleVlanHeader {
+                        pcp: 1.try_into().unwrap(),
+                        drop_eligible_indicator: false,
+                        vlan_id: 2.try_into().unwrap(),
+                        ether_type: 3.into(),
+                    },
+                };
+                let mut test = base.clone();
+                test.set_ether_type(outer_vlan_ether_type);
+                test.vlan = Some(VlanHeader::Double(double.clone()));
+
+                // ok double vlan header
+                from_x_slice_ip_variants(&test);
+
+                // len error
+                {
+                    let data = test.to_vec(&[]);
+                    for len in 0..SingleVlanHeader::LEN {
+                        let base_len = test.len(&[]) - SingleVlanHeader::LEN;
+
+                        let err = LenError {
+                            required_len: SingleVlanHeader::LEN,
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::VlanHeader,
+                            layer_start_offset: base_len,
+                        };
+                        from_slice_assert_err(
+                            &test,
+                            &data[..base_len + len],
+                            SliceError::Len(err.clone()),
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    fn from_x_slice_ip_variants(base: &TestPacket) {
+        // none
+        from_x_slice_transport_variants(base);
+
+        // ipv4
+        for fragmented in [false, true] {
+            let ipv4 = {
+                let mut ipv4 =
+                    Ipv4Header::new(0, 1, 2.into(), [3, 4, 5, 6], [7, 8, 9, 10]).unwrap();
+                ipv4.more_fragments = fragmented;
+                ipv4
+            };
+
+            {
+                let mut test = base.clone();
+                test.set_ether_type(ether_type::IPV4);
+                test.net = Some(NetHeaders::Ipv4(ipv4.clone(), Default::default()));
+
+                // ok ipv4
+                from_x_slice_transport_variants(&test);
+
+                // ipv4 len error
+                {
+                    let data = test.to_vec(&[]);
+                    for len in 0..ipv4.header_len() {
+                        let base_len = test.len(&[]) - ipv4.header_len();
+
+                        let err = LenError {
+                            required_len: ipv4.header_len(),
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv4Header,
+                            layer_start_offset: base_len,
+                        };
+                        from_slice_assert_err(
+                            &test,
+                            &data[..base_len + len],
+                            if test.link.is_some() || test.vlan.is_some() {
+                                SliceError::Len(err.clone())
+                            } else {
+                                SliceError::Len({
+                                    if len < 1 {
+                                        let mut err = err.clone();
+                                        err.required_len = 1;
+                                        err.layer = err::Layer::IpHeader;
+                                        err
+                                    } else {
+                                        err.clone()
+                                    }
+                                })
+                            },
+                        );
+                    }
+                }
+
+                // ipv4 content error
+                {
+                    let mut data = test.to_vec(&[]);
+                    let ipv4_offset = data.len() - ipv4.header_len();
+
+                    // set the ihl to 0 to trigger a content error
+                    data[ipv4_offset] = 0b1111_0000 & data[ipv4_offset];
+
+                    from_slice_assert_err(
+                        &test,
+                        &data,
+                        if test.link.is_some() || test.vlan.is_some() {
+                            SliceError::Ipv4(
+                                err::ipv4::HeaderError::HeaderLengthSmallerThanHeader { ihl: 0 },
+                            )
+                        } else {
+                            SliceError::Ip(
+                                err::ip::HeaderError::Ipv4HeaderLengthSmallerThanHeader { ihl: 0 },
+                            )
+                        },
+                    );
+                }
+            }
+
+            // ipv4 extension content error
+            {
+                let auth = IpAuthHeader::new(0.into(), 1, 2, &[]).unwrap();
+
+                let mut test = base.clone();
+                test.set_ether_type(ether_type::IPV4);
+                test.net = Some(NetHeaders::Ipv4(
+                    {
+                        let mut ipv4 = ipv4.clone();
+                        ipv4.protocol = ip_number::AUTH;
+                        ipv4
+                    },
+                    Ipv4Extensions {
+                        auth: Some(auth.clone()),
+                    },
+                ));
+                test.set_payload_len(0);
+
+                // ok ipv4 & extension
+                from_x_slice_transport_variants(&test);
+
+                // ipv4 extension len error
+                for len in 0..auth.header_len() {
+                    // set payload length
+                    let mut test = test.clone();
+                    test.set_payload_le_from_ip_on(
+                        -1 * (auth.header_len() as isize) + (len as isize),
+                    );
+
+                    let data = test.to_vec(&[]);
+                    let base_len = test.len(&[]) - auth.header_len();
+
+                    let err = LenError {
+                        required_len: auth.header_len(),
+                        len,
+                        len_source: LenSource::Ipv4HeaderTotalLen,
+                        layer: err::Layer::IpAuthHeader,
+                        layer_start_offset: base_len,
+                    };
+
+                    from_slice_assert_err(
+                        &test,
+                        &data[..base_len + len],
+                        SliceError::Len(err.clone()),
+                    );
+                }
+
+                // ipv4 extension content error
+                {
+                    let mut data = test.to_vec(&[]);
+                    let auth_offset = data.len() - auth.header_len();
+
+                    // set the icv len too smaller then allowed
+                    data[auth_offset + 1] = 0;
+
+                    // expect an error
+                    let err = err::ip_auth::HeaderError::ZeroPayloadLen;
+                    from_slice_assert_err(&test, &data, SliceError::Ipv4Exts(err.clone()));
+                }
+            }
+        }
+
+        // ipv6
+        {
+            let ipv6 = Ipv6Header {
+                traffic_class: 0,
+                flow_label: 1.try_into().unwrap(),
+                payload_length: 2,
+                next_header: 3.into(),
+                hop_limit: 4,
+                source: [0; 16],
+                destination: [0; 16],
+            };
+
+            // ipv6 header only
+            {
+                let mut test = base.clone();
+                test.set_ether_type(ether_type::IPV6);
+                test.net = Some(NetHeaders::Ipv6(ipv6.clone(), Default::default()));
+                test.set_payload_len(0);
+
+                // ok ipv6
+                from_x_slice_transport_variants(&test);
+
+                // header len ipv6
+                {
+                    let data = test.to_vec(&[]);
+                    for len in 0..ipv6.header_len() {
+                        let base_len = test.len(&[]) - ipv6.header_len();
+
+                        let err = err::LenError {
+                            required_len: ipv6.header_len(),
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::Ipv6Header,
+                            layer_start_offset: base_len,
+                        };
+
+                        from_slice_assert_err(
+                            &test,
+                            &data[..base_len + len],
+                            if test.link.is_some() || test.vlan.is_some() {
+                                SliceError::Len(err.clone())
+                            } else {
+                                SliceError::Len({
+                                    if len < 1 {
+                                        let mut err = err.clone();
+                                        err.required_len = 1;
+                                        err.layer = err::Layer::IpHeader;
+                                        err
+                                    } else {
+                                        err.clone()
+                                    }
+                                })
+                            },
+                        );
+                    }
+                }
+
+                // content error ipv6
+                {
+                    use err::ip::HeaderError::*;
+                    let mut data = test.to_vec(&[]);
+
+                    // inject an invalid ip version
+                    let base_len = data.len() - ipv6.header_len();
+                    data[base_len] = data[base_len] & 0b0000_1111;
+
+                    from_slice_assert_err(
+                        &test,
+                        &data,
+                        if test.link.is_some() || test.vlan.is_some() {
+                            SliceError::Ipv6(err::ipv6::HeaderError::UnexpectedVersion {
+                                version_number: 0,
+                            })
+                        } else {
+                            SliceError::Ip(UnsupportedIpVersion { version_number: 0 })
+                        },
+                    );
+                }
+            }
+
+            // ipv6 + extension
+            for fragment in [false, true] {
+                let auth = IpAuthHeader::new(ip_number::GGP, 1, 2, &[]).unwrap();
+                let frag = Ipv6FragmentHeader {
+                    next_header: ip_number::AUTH,
+                    fragment_offset: 0.try_into().unwrap(),
+                    more_fragments: fragment,
+                    identification: 3,
+                };
+
+                let mut test = base.clone();
+                test.set_ether_type(ether_type::IPV6);
+                test.net = Some(NetHeaders::Ipv6(
+                    {
+                        let mut ipv6 = ipv6.clone();
+                        ipv6.next_header = ip_number::IPV6_FRAG;
+                        ipv6
+                    },
+                    {
+                        let mut exts: Ipv6Extensions = Default::default();
+                        exts.fragment = Some(frag.clone());
+                        exts.auth = Some(auth.clone());
+                        exts
+                    },
+                ));
+                test.set_payload_len(0);
+
+                // ok ipv6 & extensions
+                from_x_slice_transport_variants(&test);
+
+                // ipv6 extension len error
+                for len in 0..auth.header_len() {
+                    // set payload length
+                    let mut test = test.clone();
+                    test.set_payload_le_from_ip_on(
+                        -1 * (auth.header_len() as isize) + (len as isize),
+                    );
+
+                    let data = test.to_vec(&[]);
+                    let base_len = test.len(&[]) - auth.header_len();
+
+                    let err = LenError {
+                        required_len: auth.header_len(),
+                        len,
+                        len_source: LenSource::Ipv6HeaderPayloadLen,
+                        layer: err::Layer::IpAuthHeader,
+                        layer_start_offset: base_len,
+                    };
+                    from_slice_assert_err(
+                        &test,
+                        &data[..base_len + len],
+                        SliceError::Len(err.clone()),
+                    );
+                }
+
+                // ipv6 extension content error (auth)
+                {
+                    let mut data = test.to_vec(&[]);
+                    let auth_offset = data.len() - auth.header_len();
+                    // set the icv len too smaller then allowed
+                    data[auth_offset + 1] = 0;
+
+                    let err = err::ip_auth::HeaderError::ZeroPayloadLen;
+                    from_slice_assert_err(
+                        &test,
+                        &data,
+                        SliceError::Ipv6Exts(err::ipv6_exts::HeaderError::IpAuth(err.clone())),
+                    );
+                }
+
+                // ipv6 extension content error (hop by hop not at start)
+                {
+                    let mut data = test.to_vec(&[]);
+                    let auth_offset = data.len() - auth.header_len();
+
+                    // set the next header to be a hop-by-hop header to trigger a "not at start error"
+                    data[auth_offset] = 0;
+
+                    from_slice_assert_err(
+                        &test,
+                        &data,
+                        SliceError::Ipv6Exts(err::ipv6_exts::HeaderError::HopByHopNotAtStart),
+                    );
+                }
+            }
+        }
+    }
+
+    fn from_x_slice_transport_variants(base: &TestPacket) {
+        // none
+        from_x_slice_assert_ok(base);
+
+        // transport can only be set if ip is present
+        if let Some(ip) = &base.net {
+            // udp
+            {
+                let udp = UdpHeader {
+                    source_port: 1,
+                    destination_port: 2,
+                    length: 3,
+                    checksum: 4,
+                };
+                let mut test = base.clone();
+                test.net = Some({
+                    let mut ip = match ip {
+                        NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
+                        NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
+                    };
+                    ip.set_next_headers(ip_number::UDP);
+                    ip.into()
+                });
+                test.transport = Some(TransportHeader::Udp(udp.clone()));
+                test.set_payload_len(0);
+
+                // ok decode
+                from_x_slice_assert_ok(&test);
+
+                // length error
+                if false == test.is_ip_payload_fragmented() {
+                    for len in 0..udp.header_len() {
+                        // build new test packet
+                        let mut test = test.clone();
+
+                        // set payload length
+                        test.set_payload_le_from_ip_on(len as isize);
+
+                        // generate data
+                        let data = test.to_vec(&[]);
+                        let base_len = test.len(&[]) - udp.header_len();
+
+                        let err = LenError {
+                            required_len: udp.header_len(),
+                            len,
+                            len_source: match test.net.as_ref().unwrap() {
+                                NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
+                                NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
+                            },
+                            layer: err::Layer::UdpHeader,
+                            layer_start_offset: base_len,
+                        };
+                        from_slice_assert_err(
+                            &test,
+                            &data[..base_len + len],
+                            SliceError::Len(err.clone()),
+                        );
+                    }
+                }
+            }
+
+            // tcp
+            {
+                let tcp = TcpHeader::new(1, 2, 3, 4);
+                let mut test = base.clone();
+                test.net = Some({
+                    let mut ip = match ip {
+                        NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
+                        NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
+                    };
+                    ip.set_next_headers(ip_number::TCP);
+                    ip.into()
+                });
+                test.transport = Some(TransportHeader::Tcp(tcp.clone()));
+                test.set_payload_len(0);
+
+                // ok decode
+                from_x_slice_assert_ok(&test);
+
+                // error can only occur if ip does not fragment the packet
+                if false == test.is_ip_payload_fragmented() {
+                    // length error
+                    for len in 0..(tcp.header_len() as usize) {
+                        // set payload length
+                        let mut test = test.clone();
+                        test.set_payload_le_from_ip_on(len as isize);
+
+                        let data = test.to_vec(&[]);
+                        let base_len = test.len(&[]) - (tcp.header_len() as usize);
+
+                        let err = LenError {
+                            required_len: tcp.header_len() as usize,
+                            len,
+                            len_source: match test.net.as_ref().unwrap() {
+                                NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
+                                NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
+                            },
+                            layer: err::Layer::TcpHeader,
+                            layer_start_offset: base_len,
+                        };
+                        from_slice_assert_err(
+                            &test,
+                            &data[..base_len + len],
+                            SliceError::Len(err.clone()),
+                        );
+                    }
+
+                    // content error
+                    {
+                        let mut data = test.to_vec(&[]);
+                        let base_len = test.len(&[]) - (tcp.header_len() as usize);
+
+                        // set data offset to 0 to trigger an error
+                        data[base_len + 12] = data[base_len + 12] & 0b0000_1111;
+
+                        let err = err::tcp::HeaderError::DataOffsetTooSmall { data_offset: 0 };
+                        from_slice_assert_err(&test, &data, SliceError::Tcp(err.clone()));
+                    }
+                }
+            }
+
+            // icmpv4
+            {
+                let icmpv4 =
+                    Icmpv4Header::new(Icmpv4Type::EchoReply(IcmpEchoHeader { id: 1, seq: 2 }));
+                let mut test = base.clone();
+                test.net = Some({
+                    let mut ip = match ip {
+                        NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
+                        NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
+                    };
+                    ip.set_next_headers(ip_number::ICMP);
+                    ip.into()
+                });
+                test.transport = Some(TransportHeader::Icmpv4(icmpv4.clone()));
+
+                // ok decode
+                from_x_slice_assert_ok(&test);
+
+                // length error
+                if false == test.is_ip_payload_fragmented() {
+                    for len in 0..icmpv4.header_len() {
+                        // set payload length
+                        let mut test = test.clone();
+                        test.set_payload_le_from_ip_on(len as isize);
+
+                        let data = test.to_vec(&[]);
+                        let base_len = test.len(&[]) - icmpv4.header_len();
+
+                        let err = LenError {
+                            required_len: icmpv4.header_len(),
+                            len,
+                            len_source: match test.net.as_ref().unwrap() {
+                                NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
+                                NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
+                            },
+                            layer: err::Layer::Icmpv4,
+                            layer_start_offset: base_len,
+                        };
+                        from_slice_assert_err(
+                            &test,
+                            &data[..base_len + len],
+                            SliceError::Len(err.clone()),
+                        );
+                    }
+                }
+            }
+
+            // icmpv6
+            {
+                let icmpv6 =
+                    Icmpv6Header::new(Icmpv6Type::EchoReply(IcmpEchoHeader { id: 1, seq: 2 }));
+                let mut test = base.clone();
+                test.net = Some({
+                    let mut ip = match ip {
+                        NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
+                        NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
+                    };
+                    ip.set_next_headers(ip_number::IPV6_ICMP);
+                    ip.into()
+                });
+                test.transport = Some(TransportHeader::Icmpv6(icmpv6.clone()));
+
+                // ok decode
+                from_x_slice_assert_ok(&test);
+
+                // length error
+                if false == test.is_ip_payload_fragmented() {
+                    for len in 0..icmpv6.header_len() {
+                        // set payload length
+                        let mut test = test.clone();
+                        test.set_payload_le_from_ip_on(len as isize);
+
+                        let data = test.to_vec(&[]);
+                        let base_len = test.len(&[]) - icmpv6.header_len();
+
+                        let err = LenError {
+                            required_len: icmpv6.header_len(),
+                            len,
+                            len_source: match test.net.as_ref().unwrap() {
+                                NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
+                                NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
+                            },
+                            layer: err::Layer::Icmpv6,
+                            layer_start_offset: base_len,
+                        };
+                        from_slice_assert_err(
+                            &test,
+                            &data[..base_len + len],
+                            SliceError::Len(err.clone()),
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    fn from_x_slice_assert_ok(test_base: &TestPacket) {
+        let payload = [1, 2, 3, 4];
+
+        // set length fields in ip headers
+        let test = {
+            let mut test = test_base.clone();
+            test.set_payload_len(payload.len());
+            test
+        };
+
+        // check if fragmenting
+        let is_fragmented = test.is_ip_payload_fragmented();
+
+        // write data
+        let data = test.to_vec(&payload);
+
+        // from_ethernet_slice
+        if test.link.is_some() {
+            let result = PacketHeaders::from_ethernet_slice(&data).unwrap();
+            assert_eq!(result.link, test.link);
+            assert_eq!(result.vlan, test.vlan);
+            assert_eq!(result.net, test.net);
+            if is_fragmented {
+                assert_eq!(result.transport, None);
+            } else {
+                assert_eq!(result.transport, test.transport);
+                assert_eq!(result.payload.slice(), &[1, 2, 3, 4]);
+            }
+        }
+        // from_ether_type (vlan at start)
+        if test.link.is_none() && test.vlan.is_some() {
+            for ether_type in VLAN_ETHER_TYPES {
+                let result = PacketHeaders::from_ether_type(ether_type, &data).unwrap();
+                assert_eq!(result.link, test.link);
+                assert_eq!(result.vlan, test.vlan);
+                assert_eq!(result.net, test.net);
+                if is_fragmented {
+                    assert_eq!(result.transport, None);
+                } else {
+                    assert_eq!(result.transport, test.transport);
+                    assert_eq!(result.payload.slice(), &[1, 2, 3, 4]);
+                }
+            }
+        }
+        // from_ether_type (ip at start)
+        if test.link.is_none() && test.vlan.is_none() {
+            if let Some(ip) = &test.net {
+                let result = PacketHeaders::from_ether_type(
+                    match ip {
+                        NetHeaders::Ipv4(_, _) => ether_type::IPV4,
+                        NetHeaders::Ipv6(_, _) => ether_type::IPV6,
+                    },
+                    &data,
+                )
+                .unwrap();
+                assert_eq!(result.link, test.link);
+                assert_eq!(result.vlan, test.vlan);
+                assert_eq!(result.net, test.net);
+                if is_fragmented {
+                    assert_eq!(result.transport, None);
+                } else {
+                    assert_eq!(result.transport, test.transport);
+                    assert_eq!(result.payload.slice(), &[1, 2, 3, 4]);
+                }
+            }
+        }
+        // from_ip_slice
+        if test.link.is_none() && test.vlan.is_none() && test.net.is_some() {
+            let result = PacketHeaders::from_ip_slice(&data).unwrap();
+            assert_eq!(result.link, test.link);
+            assert_eq!(result.vlan, test.vlan);
+            assert_eq!(result.net, test.net);
+            if is_fragmented {
+                assert_eq!(result.transport, None);
+            } else {
+                assert_eq!(result.transport, test.transport);
+                assert_eq!(result.payload.slice(), &[1, 2, 3, 4]);
+            }
+        }
+    }
+
+    /// Check that the given errors get triggered if presented with the given
+    /// data.
+    fn from_slice_assert_err(test: &TestPacket, data: &[u8], err: SliceError) {
+        // from_ethernet_slice
+        if test.link.is_some() {
+            assert_eq!(
+                err.clone(),
+                PacketHeaders::from_ethernet_slice(&data).unwrap_err()
+            );
+        }
+        // from_ether_type (vlan at start)
+        if test.link.is_none() && test.vlan.is_some() {
+            for ether_type in VLAN_ETHER_TYPES {
+                assert_eq!(
+                    err.clone(),
+                    PacketHeaders::from_ether_type(ether_type, &data).unwrap_err()
+                );
+            }
+        }
+        // from_ether_type (ip at start)
+        if test.link.is_none() && test.vlan.is_none() {
+            if let Some(ip) = &test.net {
+                let err = PacketHeaders::from_ether_type(
+                    match ip {
+                        NetHeaders::Ipv4(_, _) => ether_type::IPV4,
+                        NetHeaders::Ipv6(_, _) => ether_type::IPV6,
+                    },
+                    &data,
+                )
+                .unwrap_err();
+                assert_eq!(err, err.clone());
+            }
+        }
+        // from_ip_slice
+        if test.link.is_none() && test.vlan.is_none() && test.net.is_some() {
+            assert_eq!(err, PacketHeaders::from_ip_slice(&data).unwrap_err());
+        }
+    }
+}
diff --git a/src/payload_slice.rs b/src/payload_slice.rs
new file mode 100644
index 0000000..3715e54
--- /dev/null
+++ b/src/payload_slice.rs
@@ -0,0 +1,103 @@
+use crate::*;
+
+/// Payload together with an identifier the type of content.
+#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
+pub enum PayloadSlice<'a> {
+    /// Payload with it's type identified by an ether type number
+    /// (e.g. after an ethernet II or vlan header).
+    Ether(EtherPayloadSlice<'a>),
+    /// 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]),
+}
+
+impl<'a> PayloadSlice<'a> {
+    pub fn slice(&self) -> &'a [u8] {
+        match self {
+            PayloadSlice::Ether(s) => s.payload,
+            PayloadSlice::Ip(s) => s.payload,
+            PayloadSlice::Udp(s) => s,
+            PayloadSlice::Tcp(s) => s,
+            PayloadSlice::Icmpv4(s) => s,
+            PayloadSlice::Icmpv6(s) => s,
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        assert_eq!(
+            format!("Udp({:?})", &[0u8; 0]),
+            format!("{:?}", PayloadSlice::Udp(&[]))
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash_ord() {
+        let s = PayloadSlice::Udp(&[]);
+        assert_eq!(s.clone(), s);
+
+        use std::collections::hash_map::DefaultHasher;
+        use std::hash::{Hash, Hasher};
+
+        let a_hash = {
+            let mut hasher = DefaultHasher::new();
+            s.hash(&mut hasher);
+            hasher.finish()
+        };
+        let b_hash = {
+            let mut hasher = DefaultHasher::new();
+            s.clone().hash(&mut hasher);
+            hasher.finish()
+        };
+        assert_eq!(a_hash, b_hash);
+
+        use std::cmp::Ordering;
+        assert_eq!(s.clone().cmp(&s), Ordering::Equal);
+        assert_eq!(s.clone().partial_cmp(&s), Some(Ordering::Equal));
+    }
+
+    #[test]
+    fn slice() {
+        let payload = [1, 2, 3, 4];
+
+        use PayloadSlice::*;
+        assert_eq!(
+            Ether(EtherPayloadSlice {
+                ether_type: EtherType::IPV4,
+                payload: &payload
+            })
+            .slice(),
+            &payload
+        );
+        assert_eq!(
+            Ip(IpPayloadSlice {
+                ip_number: IpNumber::IPV4,
+                fragmented: false,
+                len_source: LenSource::Slice,
+                payload: &payload
+            })
+            .slice(),
+            &payload
+        );
+        assert_eq!(Udp(&payload).slice(), &payload);
+        assert_eq!(Tcp(&payload).slice(), &payload);
+        assert_eq!(Icmpv4(&payload).slice(), &payload);
+        assert_eq!(Icmpv6(&payload).slice(), &payload);
+    }
+}
diff --git a/src/sliced_packet.rs b/src/sliced_packet.rs
new file mode 100644
index 0000000..63c7382
--- /dev/null
+++ b/src/sliced_packet.rs
@@ -0,0 +1,1667 @@
+use crate::*;
+
+/// Packet slice split into multiple slices containing the different headers & payload.
+///
+/// Everything that could not be parsed is stored in a slice in the field "payload".
+///
+/// You can use
+///
+/// * [`SlicedPacket::from_ethernet`]
+/// * [`SlicedPacket::from_ether_type`]
+/// * [`SlicedPacket::from_ip`]
+///
+/// depending on your starting header to slice a packet.
+///
+/// # Examples
+///
+/// Basic usage:
+///
+///```
+/// # use etherparse::{SlicedPacket, PacketBuilder};
+/// # let builder = PacketBuilder::
+/// #    ethernet2([1,2,3,4,5,6],     //source mac
+/// #               [7,8,9,10,11,12]) //destination mac
+/// #    .ipv4([192,168,1,1], //source ip
+/// #          [192,168,1,2], //destination ip
+/// #          20)            //time to life
+/// #    .udp(21,    //source port
+/// #         1234); // destination port
+/// #    //payload of the udp packet
+/// #    let payload = [1,2,3,4,5,6,7,8];
+/// #    //get some memory to store the serialized data
+/// #    let mut packet = Vec::<u8>::with_capacity(
+/// #                            builder.size(payload.len()));
+/// #    builder.write(&mut packet, &payload).unwrap();
+/// match SlicedPacket::from_ethernet(&packet) {
+///     Err(value) => println!("Err {:?}", value),
+///     Ok(value) => {
+///         println!("link: {:?}", value.link);
+///         println!("vlan: {:?}", value.vlan);
+///         println!("net: {:?}", value.net);
+///         println!("transport: {:?}", value.transport);
+///     }
+/// }
+/// ```
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct SlicedPacket<'a> {
+    /// Ethernet II header if present.
+    pub link: Option<LinkSlice<'a>>,
+    /// Single or double vlan headers if present.
+    pub vlan: Option<VlanSlice<'a>>,
+    /// IPv4 or IPv6 header, IP extension headers & payload if present.
+    pub net: Option<NetSlice<'a>>,
+    /// TCP or UDP header & payload if present.
+    pub transport: Option<TransportSlice<'a>>,
+}
+
+impl<'a> SlicedPacket<'a> {
+    /// Separates a network packet slice into different slices containing the headers from the ethernet header downwards.
+    ///
+    /// The result is returned as a [`SlicedPacket`] struct. This function assumes the given data starts
+    /// with an ethernet II header.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    ///```
+    /// # use etherparse::{SlicedPacket, PacketBuilder};
+    /// # let builder = PacketBuilder::
+    /// #    ethernet2([1,2,3,4,5,6],     //source mac
+    /// #               [7,8,9,10,11,12]) //destination mac
+    /// #    .ipv4([192,168,1,1], //source ip
+    /// #          [192,168,1,2], //destination ip
+    /// #          20)            //time to life
+    /// #    .udp(21,    //source port
+    /// #         1234); // destination port
+    /// #    //payload of the udp packet
+    /// #    let payload = [1,2,3,4,5,6,7,8];
+    /// #    //get some memory to store the serialized data
+    /// #    let mut packet = Vec::<u8>::with_capacity(
+    /// #                            builder.size(payload.len()));
+    /// #    builder.write(&mut packet, &payload).unwrap();
+    /// match SlicedPacket::from_ethernet(&packet) {
+    ///     Err(value) => println!("Err {:?}", value),
+    ///     Ok(value) => {
+    ///         println!("link: {:?}", value.link);
+    ///         println!("vlan: {:?}", value.vlan);
+    ///         println!("net: {:?}", value.net);
+    ///         println!("transport: {:?}", value.transport);
+    ///     }
+    /// }
+    /// ```
+    pub fn from_ethernet(data: &'a [u8]) -> Result<SlicedPacket<'a>, err::packet::SliceError> {
+        SlicedPacketCursor::new(data).slice_ethernet2()
+    }
+
+    /// Separates a network packet slice into different slices containing the
+    /// headers from the Linux Cooked Capture v1 (SLL) header downwards.
+    ///
+    /// The result is returned as a [`SlicedPacket`] struct. This function
+    /// assumes the given data starts with a Linux Cooked Capture v1 (SLL)
+    /// header.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    ///```
+    /// # use etherparse::{SlicedPacket, PacketBuilder, LinuxSllPacketType};
+    /// # let builder = PacketBuilder::
+    /// #    linux_sll(LinuxSllPacketType::OTHERHOST, //packet type
+    /// #              6, //sender address valid length
+    /// #              [1,2,3,4,5,6,0,0]) //sender address with padding
+    /// #   .ipv4([192,168,1,1], //source ip
+    /// #         [192,168,1,2], //destination ip
+    /// #         20)            //time to life
+    /// #   .udp(21,    //source port
+    /// #        1234); //destination port
+    /// #    //payload of the udp packet
+    /// #    let payload = [1,2,3,4,5,6,7,8];
+    /// #    //get some memory to store the serialized data
+    /// #    let mut packet = Vec::<u8>::with_capacity(
+    /// #                            builder.size(payload.len()));
+    /// #    builder.write(&mut packet, &payload).unwrap();
+    /// match SlicedPacket::from_linux_sll(&packet) {
+    ///     Err(value) => println!("Err {:?}", value),
+    ///     Ok(value) => {
+    ///         println!("link: {:?}", value.link);
+    ///         println!("vlan: {:?}", value.vlan);
+    ///         println!("net: {:?}", value.net);
+    ///         println!("transport: {:?}", value.transport);
+    ///     }
+    /// }
+    /// ```
+    pub fn from_linux_sll(data: &'a [u8]) -> Result<SlicedPacket<'a>, err::packet::SliceError> {
+        SlicedPacketCursor::new(data).slice_linux_sll()
+    }
+
+    /// Separates a network packet slice into different slices containing the headers using
+    /// the given `ether_type` number to identify the first header.
+    ///
+    /// The result is returned as a [`SlicedPacket`] struct. Currently supported
+    /// ether type numbers are:
+    ///
+    /// * `ether_type::IPV4`
+    /// * `ether_type::IPV6`
+    /// * `ether_type::VLAN_TAGGED_FRAME`
+    /// * `ether_type::PROVIDER_BRIDGING`
+    /// * `ether_type::VLAN_DOUBLE_TAGGED_FRAME`
+    ///
+    /// If an unsupported ether type is given the given slice will be set as payload
+    /// and all other fields will be set to `None`.
+    ///
+    /// # Example
+    ///
+    /// Basic usage:
+    ///
+    ///```
+    /// # use etherparse::{Ethernet2Header, PacketBuilder};
+    /// # let builder = PacketBuilder::
+    /// #    ethernet2([1,2,3,4,5,6],     //source mac
+    /// #               [7,8,9,10,11,12]) //destination mac
+    /// #    .ipv4([192,168,1,1], //source ip
+    /// #          [192,168,1,2], //destination ip
+    /// #          20)            //time to life
+    /// #    .udp(21,    //source port
+    /// #         1234); // destination port
+    /// # // payload of the udp packet
+    /// # let payload = [1,2,3,4,5,6,7,8];
+    /// # // get some memory to store the serialized data
+    /// # let mut complete_packet = Vec::<u8>::with_capacity(
+    /// #     builder.size(payload.len())
+    /// # );
+    /// # builder.write(&mut complete_packet, &payload).unwrap();
+    /// #
+    /// # // skip ethernet 2 header so we can parse from there downwards
+    /// # let packet = &complete_packet[Ethernet2Header::LEN..];
+    /// #
+    /// use etherparse::{ether_type, SlicedPacket};
+    ///
+    /// match SlicedPacket::from_ether_type(ether_type::IPV4, packet) {
+    ///     Err(value) => println!("Err {:?}", value),
+    ///     Ok(value) => {
+    ///         println!("link: {:?}", value.link);
+    ///         println!("vlan: {:?}", value.vlan);
+    ///         println!("net: {:?}", value.net);
+    ///         println!("transport: {:?}", value.transport);
+    ///     }
+    /// }
+    /// ```
+    pub fn from_ether_type(
+        ether_type: EtherType,
+        data: &'a [u8],
+    ) -> Result<SlicedPacket<'a>, err::packet::SliceError> {
+        use ether_type::*;
+        let mut cursor = SlicedPacketCursor::new(data);
+        cursor.result.link = Some(LinkSlice::EtherPayload(EtherPayloadSlice {
+            ether_type,
+            payload: data,
+        }));
+        match ether_type {
+            IPV4 => cursor.slice_ipv4(),
+            IPV6 => cursor.slice_ipv6(),
+            VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => cursor.slice_vlan(),
+            _ => Ok(cursor.result),
+        }
+    }
+
+    /// Separates a network packet slice into different slices containing the headers from the ip header downwards.
+    ///
+    /// The result is returned as a [`SlicedPacket`] struct. This function assumes the given data starts
+    /// with an IPv4 or IPv6 header.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    ///```
+    /// # use etherparse::{SlicedPacket, PacketBuilder};
+    /// # let builder = PacketBuilder::
+    /// #    ipv4([192,168,1,1], //source ip
+    /// #         [192,168,1,2], //destination ip
+    /// #         20)            //time to life
+    /// #    .udp(21,    //source port
+    /// #         1234); // destination port
+    /// #    //payload of the udp packet
+    /// #    let payload = [1,2,3,4,5,6,7,8];
+    /// #    //get some memory to store the serialized data
+    /// #    let mut packet = Vec::<u8>::with_capacity(
+    /// #                            builder.size(payload.len()));
+    /// #    builder.write(&mut packet, &payload).unwrap();
+    /// match SlicedPacket::from_ip(&packet) {
+    ///     Err(value) => println!("Err {:?}", value),
+    ///     Ok(value) => {
+    ///         //link & vlan fields are empty when parsing from ip downwards
+    ///         assert_eq!(None, value.link);
+    ///         assert_eq!(None, value.vlan);
+    ///
+    ///         //ip & transport (udp or tcp)
+    ///         println!("net: {:?}", value.net);
+    ///         println!("transport: {:?}", value.transport);
+    ///     }
+    /// }
+    /// ```
+    pub fn from_ip(data: &'a [u8]) -> Result<SlicedPacket<'a>, err::packet::SliceError> {
+        SlicedPacketCursor::new(data).slice_ip()
+    }
+
+    /// If the slice in the `payload` field contains an ethernet payload
+    /// this method returns the ether type number describing the payload type.
+    ///
+    /// The ether type number can come from an ethernet II header or a
+    /// VLAN header depending on which headers are present.
+    ///
+    /// In case that `ip` and/or `transport` fields are the filled None
+    /// is returned, as the payload contents then are defined by a
+    /// lower layer protocol described in these fields.
+    pub fn payload_ether_type(&self) -> Option<EtherType> {
+        if self.net.is_some() || self.transport.is_some() {
+            None
+        } else if let Some(vlan) = &self.vlan {
+            use VlanSlice::*;
+            match vlan {
+                SingleVlan(s) => Some(s.ether_type()),
+                DoubleVlan(d) => Some(d.inner().ether_type()),
+            }
+        } else if let Some(link) = &self.link {
+            use LinkSlice::*;
+            match link {
+                Ethernet2(eth) => Some(eth.ether_type()),
+                LinkSlice::LinuxSll(e) => match e.protocol_type() {
+                    LinuxSllProtocolType::EtherType(EtherType(v))
+                    | LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType(
+                        v,
+                    )) => Some(EtherType(v)),
+                    _ => None,
+                },
+                EtherPayload(e) => Some(e.ether_type),
+                LinkSlice::LinuxSllPayload(e) => match e.protocol_type {
+                    LinuxSllProtocolType::EtherType(EtherType(v))
+                    | LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType(
+                        v,
+                    )) => Some(EtherType(v)),
+                    _ => None,
+                },
+            }
+        } else {
+            None
+        }
+    }
+
+    /// Returns the last ether payload of the packet (if one is present).
+    ///
+    /// If VLAN header is present the payload after the most inner VLAN
+    /// header is returned and if there is no VLAN header is present in the
+    /// link field is returned.
+    pub fn ether_payload(&self) -> Option<EtherPayloadSlice<'a>> {
+        if let Some(vlan) = self.vlan.as_ref() {
+            match vlan {
+                VlanSlice::SingleVlan(s) => Some(s.payload()),
+                VlanSlice::DoubleVlan(s) => Some(s.payload()),
+            }
+        } else if let Some(link) = self.link.as_ref() {
+            match link {
+                LinkSlice::Ethernet2(e) => Some(e.payload()),
+                LinkSlice::LinuxSll(e) => match e.protocol_type() {
+                    LinuxSllProtocolType::EtherType(_)
+                    | LinuxSllProtocolType::LinuxNonstandardEtherType(_) => {
+                        Some(EtherPayloadSlice::try_from(e.payload()).ok()?)
+                    }
+                    _ => None,
+                },
+                LinkSlice::EtherPayload(e) => Some(e.clone()),
+                LinkSlice::LinuxSllPayload(e) => match e.protocol_type {
+                    LinuxSllProtocolType::EtherType(_)
+                    | LinuxSllProtocolType::LinuxNonstandardEtherType(_) => {
+                        Some(EtherPayloadSlice::try_from(e.clone()).ok()?)
+                    }
+                    _ => None,
+                },
+            }
+        } else {
+            None
+        }
+    }
+
+    /// Return the IP payload after the the IP header and the IP extension
+    /// headers (if one is present).
+    pub fn ip_payload(&self) -> Option<&IpPayloadSlice<'a>> {
+        if let Some(net) = self.net.as_ref() {
+            use NetSlice::*;
+            match net {
+                Ipv4(v) => Some(v.payload()),
+                Ipv6(v) => Some(v.payload()),
+            }
+        } else {
+            None
+        }
+    }
+
+    /// Returns true if `net` contains an fragmented IPv4 or IPv6 payload.
+    pub fn is_ip_payload_fragmented(&self) -> bool {
+        use NetSlice::*;
+        match &self.net {
+            Some(Ipv4(v)) => v.is_payload_fragmented(),
+            Some(Ipv6(v)) => v.is_payload_fragmented(),
+            None => false,
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::err::{packet::SliceError, Layer, LenError};
+    use crate::test_gens::*;
+    use crate::test_packet::TestPacket;
+    use proptest::prelude::*;
+
+    const VLAN_ETHER_TYPES: [EtherType; 3] = [
+        ether_type::VLAN_TAGGED_FRAME,
+        ether_type::PROVIDER_BRIDGING,
+        ether_type::VLAN_DOUBLE_TAGGED_FRAME,
+    ];
+
+    #[test]
+    fn clone_eq() {
+        let header = SlicedPacket {
+            link: None,
+            vlan: None,
+            net: None,
+            transport: None,
+        };
+        assert_eq!(header.clone(), header);
+    }
+
+    #[test]
+    fn debug() {
+        use alloc::format;
+        let header = SlicedPacket {
+            link: None,
+            vlan: None,
+            net: None,
+            transport: None,
+        };
+        assert_eq!(
+            format!("{:?}", header),
+            format!(
+                "SlicedPacket {{ link: {:?}, vlan: {:?}, net: {:?}, transport: {:?} }}",
+                header.link, header.vlan, header.net, header.transport,
+            )
+        );
+    }
+
+    #[test]
+    fn ether_payload() {
+        use alloc::vec::*;
+
+        // no content
+        assert_eq!(
+            SlicedPacket {
+                link: None,
+                vlan: None,
+                net: None,
+                transport: None,
+            }
+            .ether_payload(),
+            None
+        );
+
+        // only ethernet header II
+        {
+            let payload = [1, 2, 3, 4];
+            let mut buf = Vec::with_capacity(Ethernet2Header::LEN + 4);
+            buf.extend_from_slice(
+                &Ethernet2Header {
+                    ether_type: EtherType::WAKE_ON_LAN,
+                    ..Default::default()
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(&payload);
+            assert_eq!(
+                SlicedPacket::from_ethernet(&buf).unwrap().ether_payload(),
+                Some(EtherPayloadSlice {
+                    ether_type: EtherType::WAKE_ON_LAN,
+                    payload: &payload
+                })
+            );
+        }
+
+        // ether type payload
+        {
+            let payload = [1, 2, 3, 4];
+            assert_eq!(
+                SlicedPacket {
+                    link: Some(LinkSlice::EtherPayload(EtherPayloadSlice {
+                        ether_type: EtherType::WAKE_ON_LAN,
+                        payload: &payload
+                    })),
+                    vlan: None,
+                    net: None,
+                    transport: None,
+                }
+                .ether_payload(),
+                Some(EtherPayloadSlice {
+                    ether_type: EtherType::WAKE_ON_LAN,
+                    payload: &payload
+                })
+            );
+        }
+
+        // only linux_sll payload
+        {
+            let payload = [1, 2, 3, 4];
+            let mut buf = Vec::with_capacity(LinuxSllHeader::LEN + 4);
+            buf.extend_from_slice(
+                &LinuxSllHeader {
+                    packet_type: LinuxSllPacketType::HOST,
+                    arp_hrd_type: ArpHardwareId::ETHER,
+                    sender_address_valid_length: 6,
+                    sender_address: [1, 2, 3, 4, 5, 6, 0, 0],
+                    protocol_type: LinuxSllProtocolType::EtherType(EtherType::WAKE_ON_LAN),
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(&payload);
+            assert_eq!(
+                SlicedPacket::from_linux_sll(&buf).unwrap().ether_payload(),
+                Some(EtherPayloadSlice {
+                    ether_type: EtherType::WAKE_ON_LAN,
+                    payload: &payload
+                })
+            );
+        }
+
+        // ether type payload
+        {
+            let payload = [1, 2, 3, 4];
+            assert_eq!(
+                SlicedPacket {
+                    link: Some(LinkSlice::LinuxSllPayload(LinuxSllPayloadSlice {
+                        protocol_type: LinuxSllProtocolType::EtherType(EtherType::WAKE_ON_LAN),
+                        payload: &payload
+                    })),
+                    vlan: None,
+                    net: None,
+                    transport: None,
+                }
+                .ether_payload(),
+                Some(EtherPayloadSlice {
+                    ether_type: EtherType::WAKE_ON_LAN,
+                    payload: &payload
+                })
+            );
+        }
+
+        // single vlan header
+        {
+            let payload = [1, 2, 3, 4];
+            let mut buf = Vec::with_capacity(Ethernet2Header::LEN + SingleVlanHeader::LEN + 4);
+            buf.extend_from_slice(
+                &Ethernet2Header {
+                    ether_type: EtherType::VLAN_TAGGED_FRAME,
+                    ..Default::default()
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(
+                &SingleVlanHeader {
+                    ether_type: EtherType::WAKE_ON_LAN,
+                    ..Default::default()
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(&payload);
+            assert_eq!(
+                SlicedPacket::from_ethernet(&buf).unwrap().ether_payload(),
+                Some(EtherPayloadSlice {
+                    ether_type: EtherType::WAKE_ON_LAN,
+                    payload: &payload
+                })
+            );
+        }
+
+        // double vlan header
+        {
+            let payload = [1, 2, 3, 4];
+            let mut buf = Vec::with_capacity(Ethernet2Header::LEN + SingleVlanHeader::LEN * 2 + 4);
+            buf.extend_from_slice(
+                &Ethernet2Header {
+                    ether_type: EtherType::VLAN_DOUBLE_TAGGED_FRAME,
+                    ..Default::default()
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(
+                &SingleVlanHeader {
+                    ether_type: EtherType::VLAN_TAGGED_FRAME,
+                    ..Default::default()
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(
+                &SingleVlanHeader {
+                    ether_type: EtherType::WAKE_ON_LAN,
+                    ..Default::default()
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(&payload);
+            assert_eq!(
+                SlicedPacket::from_ethernet(&buf).unwrap().ether_payload(),
+                Some(EtherPayloadSlice {
+                    ether_type: EtherType::WAKE_ON_LAN,
+                    payload: &payload
+                })
+            );
+        }
+    }
+
+    #[test]
+    fn ip_payload() {
+        use alloc::vec::*;
+
+        // no content
+        assert_eq!(
+            SlicedPacket {
+                link: None,
+                vlan: None,
+                net: None,
+                transport: None,
+            }
+            .ip_payload(),
+            None
+        );
+
+        // ipv4
+        {
+            let payload = [1, 2, 3, 4];
+            let mut buf = Vec::with_capacity(Ipv4Header::MIN_LEN + 4);
+            buf.extend_from_slice(
+                &Ipv4Header {
+                    protocol: IpNumber::ARIS,
+                    total_len: Ipv4Header::MIN_LEN_U16 + 4,
+                    ..Default::default()
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(&payload);
+            assert_eq!(
+                SlicedPacket::from_ip(&buf).unwrap().ip_payload(),
+                Some(&IpPayloadSlice {
+                    payload: &payload,
+                    ip_number: IpNumber::ARIS,
+                    fragmented: false,
+                    len_source: LenSource::Ipv4HeaderTotalLen,
+                })
+            );
+        }
+
+        // ipv6
+        {
+            let payload = [1, 2, 3, 4];
+            let mut buf = Vec::with_capacity(Ipv6Header::LEN + 4);
+            buf.extend_from_slice(
+                &Ipv6Header {
+                    payload_length: 4,
+                    next_header: IpNumber::ARGUS,
+                    ..Default::default()
+                }
+                .to_bytes(),
+            );
+            buf.extend_from_slice(&payload);
+            assert_eq!(
+                SlicedPacket::from_ip(&buf).unwrap().ip_payload(),
+                Some(&IpPayloadSlice {
+                    payload: &payload,
+                    ip_number: IpNumber::ARGUS,
+                    fragmented: false,
+                    len_source: LenSource::Ipv6HeaderPayloadLen,
+                })
+            );
+        }
+    }
+
+    #[test]
+    fn from_x_slice() {
+        // no eth
+        from_x_slice_vlan_variants(&TestPacket {
+            link: None,
+            vlan: None,
+            net: None,
+            transport: None,
+        });
+
+        // eth payload
+        {
+            let data = [1, 2, 3, 4];
+            let result = SlicedPacket::from_ether_type(EtherType(0x8221), &data).unwrap();
+            assert_eq!(
+                result,
+                SlicedPacket {
+                    link: Some(LinkSlice::EtherPayload(EtherPayloadSlice {
+                        ether_type: EtherType(0x8221),
+                        payload: &data
+                    })),
+                    vlan: None,
+                    net: None,
+                    transport: None
+                }
+            );
+        }
+
+        // eth
+        {
+            let eth = Ethernet2Header {
+                source: [1, 2, 3, 4, 5, 6],
+                destination: [1, 2, 3, 4, 5, 6],
+                ether_type: 0.into(),
+            };
+            let test = TestPacket {
+                link: Some(LinkHeader::Ethernet2(eth.clone())),
+                vlan: None,
+                net: None,
+                transport: None,
+            };
+
+            // ok ethernet header (with unknown next)
+            from_x_slice_vlan_variants(&test);
+
+            // eth len error
+            {
+                let data = test.to_vec(&[]);
+                for len in 0..data.len() {
+                    let err = LenError {
+                        required_len: eth.header_len(),
+                        len,
+                        len_source: LenSource::Slice,
+                        layer: Layer::Ethernet2Header,
+                        layer_start_offset: 0,
+                    };
+
+                    from_slice_assert_err(&test, &data[..len], SliceError::Len(err.clone()));
+                }
+            }
+        }
+
+        // linux_sll
+        {
+            let linux_sll = LinuxSllHeader {
+                packet_type: LinuxSllPacketType::HOST,
+                arp_hrd_type: ArpHardwareId::ETHER,
+                sender_address_valid_length: 6,
+                sender_address: [1, 2, 3, 4, 5, 6, 0, 0],
+                protocol_type: LinuxSllProtocolType::EtherType(EtherType::WAKE_ON_LAN),
+            };
+            let test = TestPacket {
+                link: Some(LinkHeader::LinuxSll(linux_sll.clone())),
+                vlan: None,
+                net: None,
+                transport: None,
+            };
+
+            // eth len error
+            {
+                let data = test.to_vec(&[]);
+                for len in 0..data.len() {
+                    let err = LenError {
+                        required_len: linux_sll.header_len(),
+                        len,
+                        len_source: LenSource::Slice,
+                        layer: Layer::LinuxSllHeader,
+                        layer_start_offset: 0,
+                    };
+
+                    from_slice_assert_err(&test, &data[..len], SliceError::Len(err.clone()));
+                }
+            }
+        }
+    }
+
+    fn from_x_slice_vlan_variants(base: &TestPacket) {
+        // none
+        from_x_slice_ip_variants(base);
+
+        // single vlan header
+        {
+            let single = SingleVlanHeader {
+                pcp: 1.try_into().unwrap(),
+                drop_eligible_indicator: false,
+                vlan_id: 2.try_into().unwrap(),
+                ether_type: 3.into(),
+            };
+
+            for vlan_ether_type in VLAN_ETHER_TYPES {
+                let mut test = base.clone();
+                test.set_ether_type(vlan_ether_type);
+                test.vlan = Some(VlanHeader::Single(single.clone()));
+
+                // ok vlan header
+                from_x_slice_ip_variants(&test);
+
+                // len error
+                {
+                    let data = test.to_vec(&[]);
+                    for len in 0..single.header_len() {
+                        let base_len = test.len(&[]) - single.header_len();
+
+                        let err = LenError {
+                            required_len: single.header_len(),
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: Layer::VlanHeader,
+                            layer_start_offset: base_len,
+                        };
+                        from_slice_assert_err(
+                            &test,
+                            &data[..base_len + len],
+                            SliceError::Len(err.clone()),
+                        );
+                    }
+                }
+            }
+        }
+
+        // double vlan header
+        for outer_vlan_ether_type in VLAN_ETHER_TYPES {
+            for inner_vlan_ether_type in VLAN_ETHER_TYPES {
+                let double = DoubleVlanHeader {
+                    outer: SingleVlanHeader {
+                        pcp: 1.try_into().unwrap(),
+                        drop_eligible_indicator: false,
+                        vlan_id: 2.try_into().unwrap(),
+                        ether_type: inner_vlan_ether_type,
+                    },
+                    inner: SingleVlanHeader {
+                        pcp: 1.try_into().unwrap(),
+                        drop_eligible_indicator: false,
+                        vlan_id: 2.try_into().unwrap(),
+                        ether_type: 3.into(),
+                    },
+                };
+                let mut test = base.clone();
+                test.set_ether_type(outer_vlan_ether_type);
+                test.vlan = Some(VlanHeader::Double(double.clone()));
+
+                // ok double vlan header
+                from_x_slice_ip_variants(&test);
+
+                // len error
+                {
+                    let data = test.to_vec(&[]);
+                    for len in 0..SingleVlanHeader::LEN {
+                        let base_len = test.len(&[]) - SingleVlanHeader::LEN;
+
+                        let err = LenError {
+                            required_len: SingleVlanHeader::LEN,
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: Layer::VlanHeader,
+                            layer_start_offset: base_len,
+                        };
+                        from_slice_assert_err(
+                            &test,
+                            &data[..base_len + len],
+                            SliceError::Len(err.clone()),
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    fn from_x_slice_ip_variants(base: &TestPacket) {
+        // none
+        from_x_slice_transport_variants(base);
+
+        // ipv4
+        for fragmented in [false, true] {
+            let ipv4 = {
+                let mut ipv4 =
+                    Ipv4Header::new(0, 1, 2.into(), [3, 4, 5, 6], [7, 8, 9, 10]).unwrap();
+                ipv4.more_fragments = fragmented;
+                ipv4
+            };
+
+            {
+                let mut test = base.clone();
+                test.set_ether_type(ether_type::IPV4);
+                test.net = Some(NetHeaders::Ipv4(ipv4.clone(), Default::default()));
+                test.set_payload_len(0);
+
+                // ok ipv4
+                from_x_slice_transport_variants(&test);
+
+                // ipv4 len error
+                {
+                    let data = test.to_vec(&[]);
+                    for len in 0..ipv4.header_len() {
+                        let base_len = test.len(&[]) - ipv4.header_len();
+
+                        let err = LenError {
+                            required_len: ipv4.header_len(),
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: Layer::Ipv4Header,
+                            layer_start_offset: base_len,
+                        };
+                        from_slice_assert_err(
+                            &test,
+                            &data[..base_len + len],
+                            if test.link.is_some() || test.vlan.is_some() {
+                                SliceError::Len(err.clone())
+                            } else {
+                                SliceError::Len({
+                                    if len < 1 {
+                                        let mut err = err.clone();
+                                        err.required_len = 1;
+                                        err.layer = Layer::IpHeader;
+                                        err
+                                    } else {
+                                        err.clone()
+                                    }
+                                })
+                            },
+                        );
+                    }
+                }
+
+                // ipv4 content error (ihl length too small)
+                {
+                    use err::ip::HeaderError::*;
+
+                    let mut data = test.to_vec(&[]);
+                    let ipv4_offset = data.len() - ipv4.header_len();
+
+                    // set the ihl to 0 to trigger a content error
+                    data[ipv4_offset] = 0b1111_0000 & data[ipv4_offset];
+
+                    from_slice_assert_err(
+                        &test,
+                        &data,
+                        if test.link.is_some() || test.vlan.is_some() {
+                            SliceError::Ipv4(
+                                err::ipv4::HeaderError::HeaderLengthSmallerThanHeader { ihl: 0 },
+                            )
+                        } else {
+                            SliceError::Ip(Ipv4HeaderLengthSmallerThanHeader { ihl: 0 })
+                        },
+                    );
+                }
+
+                // ipv4 content error (total length too small)
+                {
+                    let mut data = test.to_vec(&[]);
+                    let ipv4_offset = data.len() - ipv4.header_len();
+
+                    // set the total length to 0 to trigger a content error
+                    data[ipv4_offset + 2] = 0;
+                    data[ipv4_offset + 3] = 0;
+
+                    let err = LenError {
+                        required_len: ipv4.header_len(),
+                        len: 0,
+                        len_source: LenSource::Ipv4HeaderTotalLen,
+                        layer: Layer::Ipv4Packet,
+                        layer_start_offset: {
+                            test.link.as_ref().map(|h| h.header_len()).unwrap_or(0)
+                                + test.vlan.as_ref().map(|h| h.header_len()).unwrap_or(0)
+                        },
+                    };
+
+                    from_slice_assert_err(&test, &data, SliceError::Len(err.clone()));
+                }
+            }
+
+            // ipv4 extension content error
+            {
+                let auth = IpAuthHeader::new(0.into(), 1, 2, &[]).unwrap();
+
+                let mut test = base.clone();
+                test.set_ether_type(ether_type::IPV4);
+                test.net = Some(NetHeaders::Ipv4(
+                    {
+                        let mut ipv4 = ipv4.clone();
+                        ipv4.protocol = ip_number::AUTH;
+                        ipv4
+                    },
+                    Ipv4Extensions {
+                        auth: Some(auth.clone()),
+                    },
+                ));
+                test.set_payload_len(0);
+
+                // ok ipv4 & extension
+                from_x_slice_transport_variants(&test);
+
+                // ipv4 extension len error
+                for len in 0..auth.header_len() {
+                    // set payload length
+                    let mut test = test.clone();
+                    test.set_payload_le_from_ip_on(
+                        -1 * (auth.header_len() as isize) + (len as isize),
+                    );
+
+                    let data = test.to_vec(&[]);
+                    let base_len = test.len(&[]) - auth.header_len();
+
+                    let err = LenError {
+                        required_len: auth.header_len(),
+                        len,
+                        len_source: LenSource::Ipv4HeaderTotalLen,
+                        layer: Layer::IpAuthHeader,
+                        layer_start_offset: base_len,
+                    };
+
+                    from_slice_assert_err(
+                        &test,
+                        &data[..base_len + len],
+                        SliceError::Len(err.clone()),
+                    );
+                }
+
+                // ipv4 extension content error
+                {
+                    let mut data = test.to_vec(&[]);
+                    let auth_offset = data.len() - auth.header_len();
+
+                    // set the icv len too smaller then allowed
+                    data[auth_offset + 1] = 0;
+
+                    // expect an error
+                    let err = err::ip_auth::HeaderError::ZeroPayloadLen;
+                    from_slice_assert_err(&test, &data, SliceError::Ipv4Exts(err.clone()));
+                }
+            }
+        }
+
+        // ipv6
+        {
+            let ipv6 = Ipv6Header {
+                traffic_class: 0,
+                flow_label: 1.try_into().unwrap(),
+                payload_length: 2,
+                next_header: 3.into(),
+                hop_limit: 4,
+                source: [0; 16],
+                destination: [0; 16],
+            };
+
+            // ipv6 header only
+            {
+                let mut test = base.clone();
+                test.set_ether_type(ether_type::IPV6);
+                test.net = Some(NetHeaders::Ipv6(ipv6.clone(), Default::default()));
+                test.set_payload_len(0);
+
+                // ok ipv6
+                from_x_slice_transport_variants(&test);
+
+                // header len ipv6
+                {
+                    let data = test.to_vec(&[]);
+                    for len in 0..ipv6.header_len() {
+                        let base_len = test.len(&[]) - ipv6.header_len();
+
+                        let err = err::LenError {
+                            required_len: ipv6.header_len(),
+                            len,
+                            len_source: LenSource::Slice,
+                            layer: Layer::Ipv6Header,
+                            layer_start_offset: base_len,
+                        };
+
+                        from_slice_assert_err(
+                            &test,
+                            &data[..base_len + len],
+                            if test.link.is_some() || test.vlan.is_some() {
+                                SliceError::Len(err.clone())
+                            } else {
+                                SliceError::Len({
+                                    if len < 1 {
+                                        let mut err = err.clone();
+                                        err.required_len = 1;
+                                        err.layer = Layer::IpHeader;
+                                        err
+                                    } else {
+                                        err.clone()
+                                    }
+                                })
+                            },
+                        );
+                    }
+                }
+
+                // content error ipv6
+                {
+                    use err::ip::HeaderError::*;
+
+                    let mut data = test.to_vec(&[]);
+
+                    // inject an invalid ip version
+                    let base_len = data.len() - ipv6.header_len();
+                    data[base_len] = data[base_len] & 0b0000_1111;
+
+                    from_slice_assert_err(
+                        &test,
+                        &data,
+                        if test.link.is_some() || test.vlan.is_some() {
+                            SliceError::Ipv6(err::ipv6::HeaderError::UnexpectedVersion {
+                                version_number: 0,
+                            })
+                        } else {
+                            SliceError::Ip(UnsupportedIpVersion { version_number: 0 })
+                        },
+                    );
+                }
+            }
+
+            // ipv6 + extension
+            for fragment in [false, true] {
+                let auth = IpAuthHeader::new(ip_number::GGP, 1, 2, &[]).unwrap();
+                let frag = Ipv6FragmentHeader {
+                    next_header: ip_number::AUTH,
+                    fragment_offset: 0.try_into().unwrap(),
+                    more_fragments: fragment,
+                    identification: 3,
+                };
+
+                let mut test = base.clone();
+                test.set_ether_type(ether_type::IPV6);
+                test.net = Some(NetHeaders::Ipv6(
+                    {
+                        let mut ipv6 = ipv6.clone();
+                        ipv6.next_header = ip_number::IPV6_FRAG;
+                        ipv6
+                    },
+                    {
+                        let mut exts: Ipv6Extensions = Default::default();
+                        exts.fragment = Some(frag.clone());
+                        exts.auth = Some(auth.clone());
+                        exts
+                    },
+                ));
+                test.set_payload_len(0);
+
+                // ok ipv6 & extensions
+                from_x_slice_transport_variants(&test);
+
+                // ipv6 extension len error
+                for len in 0..auth.header_len() {
+                    // set payload length
+                    let mut test = test.clone();
+                    test.set_payload_le_from_ip_on(
+                        -1 * (auth.header_len() as isize) + (len as isize),
+                    );
+
+                    let data = test.to_vec(&[]);
+                    let base_len = test.len(&[]) - auth.header_len();
+
+                    let err = LenError {
+                        required_len: auth.header_len(),
+                        len,
+                        len_source: LenSource::Ipv6HeaderPayloadLen,
+                        layer: Layer::IpAuthHeader,
+                        layer_start_offset: base_len,
+                    };
+                    from_slice_assert_err(
+                        &test,
+                        &data[..base_len + len],
+                        SliceError::Len(err.clone()),
+                    );
+                }
+
+                // ipv6 extension content error (auth)
+                {
+                    let mut data = test.to_vec(&[]);
+                    let auth_offset = data.len() - auth.header_len();
+                    // set the icv len too smaller then allowed
+                    data[auth_offset + 1] = 0;
+
+                    let err = err::ip_auth::HeaderError::ZeroPayloadLen;
+                    from_slice_assert_err(
+                        &test,
+                        &data,
+                        SliceError::Ipv6Exts(err::ipv6_exts::HeaderError::IpAuth(err.clone())),
+                    );
+                }
+
+                // ipv6 extension content error (hop by hop not at start)
+                {
+                    let mut data = test.to_vec(&[]);
+                    let auth_offset = data.len() - auth.header_len();
+
+                    // set the next header to be a hop-by-hop header to trigger a "not at start error"
+                    data[auth_offset] = 0;
+
+                    from_slice_assert_err(
+                        &test,
+                        &data,
+                        SliceError::Ipv6Exts(err::ipv6_exts::HeaderError::HopByHopNotAtStart),
+                    );
+                }
+            }
+        }
+    }
+
+    fn from_x_slice_transport_variants(base: &TestPacket) {
+        // none
+        from_x_slice_assert_ok(base);
+
+        // transport can only be set if ip is present
+        if let Some(ip) = &base.net {
+            // udp
+            {
+                let udp = UdpHeader {
+                    source_port: 1,
+                    destination_port: 2,
+                    length: 3,
+                    checksum: 4,
+                };
+                let mut test = base.clone();
+                test.net = Some({
+                    let mut ip = match ip {
+                        NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
+                        NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
+                    };
+                    ip.set_next_headers(ip_number::UDP);
+                    ip.into()
+                });
+                test.transport = Some(TransportHeader::Udp(udp.clone()));
+                test.set_payload_len(0);
+
+                // ok decode
+                from_x_slice_assert_ok(&test);
+
+                // length error
+                if false == test.is_ip_payload_fragmented() {
+                    for len in 0..udp.header_len() {
+                        // build new test packet
+                        let mut test = test.clone();
+
+                        // set payload length
+                        test.set_payload_le_from_ip_on(len as isize);
+
+                        // generate data
+                        let data = test.to_vec(&[]);
+
+                        let base_len = test.len(&[]) - udp.header_len();
+                        let err = LenError {
+                            required_len: udp.header_len(),
+                            len,
+                            len_source: match test.net.as_ref().unwrap() {
+                                NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
+                                NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
+                            },
+                            layer: Layer::UdpHeader,
+                            layer_start_offset: base_len,
+                        };
+                        from_slice_assert_err(
+                            &test,
+                            &data[..base_len + len],
+                            SliceError::Len(err.clone()),
+                        );
+                    }
+                }
+            }
+
+            // tcp
+            {
+                let tcp = TcpHeader::new(1, 2, 3, 4);
+                let mut test = base.clone();
+                test.net = Some({
+                    let mut ip = match ip {
+                        NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
+                        NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
+                    };
+                    ip.set_next_headers(ip_number::TCP);
+                    ip.into()
+                });
+                test.transport = Some(TransportHeader::Tcp(tcp.clone()));
+                test.set_payload_len(0);
+
+                // ok decode
+                from_x_slice_assert_ok(&test);
+
+                // error can only occur if ip does not fragment the packet
+                if false == test.is_ip_payload_fragmented() {
+                    // length error
+                    {
+                        for len in 0..(tcp.header_len() as usize) {
+                            // set payload length
+                            let mut test = test.clone();
+                            test.set_payload_le_from_ip_on(len as isize);
+
+                            let data = test.to_vec(&[]);
+                            let base_len = test.len(&[]) - (tcp.header_len() as usize);
+
+                            let err = LenError {
+                                required_len: tcp.header_len() as usize,
+                                len,
+                                len_source: match test.net.as_ref().unwrap() {
+                                    NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
+                                    NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
+                                },
+                                layer: Layer::TcpHeader,
+                                layer_start_offset: base_len,
+                            };
+                            from_slice_assert_err(
+                                &test,
+                                &data[..base_len + len],
+                                SliceError::Len(err.clone()),
+                            );
+                        }
+                    }
+
+                    // content error
+                    {
+                        let mut data = test.to_vec(&[]);
+                        let base_len = test.len(&[]) - (tcp.header_len() as usize);
+
+                        // set data offset to 0 to trigger an error
+                        data[base_len + 12] = data[base_len + 12] & 0b0000_1111;
+
+                        let err = err::tcp::HeaderError::DataOffsetTooSmall { data_offset: 0 };
+                        from_slice_assert_err(&test, &data, SliceError::Tcp(err.clone()));
+                    }
+                }
+            }
+
+            // icmpv4
+            {
+                let icmpv4 =
+                    Icmpv4Header::new(Icmpv4Type::EchoReply(IcmpEchoHeader { id: 1, seq: 2 }));
+                let mut test = base.clone();
+                test.net = Some({
+                    let mut ip = match ip {
+                        NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
+                        NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
+                    };
+                    ip.set_next_headers(ip_number::ICMP);
+                    ip.into()
+                });
+                test.transport = Some(TransportHeader::Icmpv4(icmpv4.clone()));
+                test.set_payload_len(0);
+
+                // ok decode
+                from_x_slice_assert_ok(&test);
+
+                // length error
+                if false == test.is_ip_payload_fragmented() {
+                    for len in 0..icmpv4.header_len() {
+                        // set payload length
+                        let mut test = test.clone();
+                        test.set_payload_le_from_ip_on(len as isize);
+
+                        let data = test.to_vec(&[]);
+                        let base_len = test.len(&[]) - icmpv4.header_len();
+
+                        let err = LenError {
+                            required_len: icmpv4.header_len(),
+                            len,
+                            len_source: match test.net.as_ref().unwrap() {
+                                NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
+                                NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
+                            },
+                            layer: Layer::Icmpv4,
+                            layer_start_offset: base_len,
+                        };
+                        from_slice_assert_err(
+                            &test,
+                            &data[..base_len + len],
+                            SliceError::Len(err.clone()),
+                        );
+                    }
+                }
+            }
+
+            // icmpv6
+            {
+                let icmpv6 =
+                    Icmpv6Header::new(Icmpv6Type::EchoReply(IcmpEchoHeader { id: 1, seq: 2 }));
+                let mut test = base.clone();
+                test.net = Some({
+                    let mut ip = match ip {
+                        NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
+                        NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
+                    };
+                    ip.set_next_headers(ip_number::IPV6_ICMP);
+                    ip.into()
+                });
+                test.transport = Some(TransportHeader::Icmpv6(icmpv6.clone()));
+                test.set_payload_len(0);
+
+                // ok decode
+                from_x_slice_assert_ok(&test);
+
+                // length error
+                if false == test.is_ip_payload_fragmented() {
+                    for len in 0..icmpv6.header_len() {
+                        // set payload length
+                        let mut test = test.clone();
+                        test.set_payload_le_from_ip_on(len as isize);
+
+                        let data = test.to_vec(&[]);
+                        let base_len = test.len(&[]) - icmpv6.header_len();
+
+                        let err = LenError {
+                            required_len: icmpv6.header_len(),
+                            len,
+                            len_source: match test.net.as_ref().unwrap() {
+                                NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
+                                NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
+                            },
+                            layer: Layer::Icmpv6,
+                            layer_start_offset: base_len,
+                        };
+                        from_slice_assert_err(
+                            &test,
+                            &data[..base_len + len],
+                            SliceError::Len(err.clone()),
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    fn from_x_slice_assert_ok(test_base: &TestPacket) {
+        fn assert_test_result(test: &TestPacket, expected_payload: &[u8], result: &SlicedPacket) {
+            // check if fragmenting
+            let is_fragmented = test.is_ip_payload_fragmented();
+
+            // check headers
+            assert_eq!(
+                test.link,
+                match result.link.as_ref() {
+                    Some(s) => match s {
+                        LinkSlice::Ethernet2(e) => Some(LinkHeader::Ethernet2(e.to_header())),
+                        LinkSlice::LinuxSll(e) => Some(LinkHeader::LinuxSll(e.to_header())),
+                        LinkSlice::EtherPayload(_) => None,
+                        LinkSlice::LinuxSllPayload(_) => None,
+                    },
+                    None => None,
+                }
+            );
+            assert_eq!(test.vlan, result.vlan.as_ref().map(|e| e.to_header()));
+            assert_eq!(
+                test.net,
+                result.net.as_ref().map(|s: &NetSlice| -> NetHeaders {
+                    match s {
+                        NetSlice::Ipv4(ipv4) => NetHeaders::Ipv4(
+                            ipv4.header().to_header(),
+                            ipv4.extensions().to_header(),
+                        ),
+                        NetSlice::Ipv6(ipv6) => NetHeaders::Ipv6(
+                            ipv6.header().to_header(),
+                            Ipv6Extensions::from_slice(
+                                ipv6.header().next_header(),
+                                ipv6.extensions().slice(),
+                            )
+                            .unwrap()
+                            .0,
+                        ),
+                    }
+                })
+            );
+
+            // check transport header & payload
+            if is_fragmented {
+                assert_eq!(result.transport, None);
+            } else {
+                use TransportHeader as H;
+                use TransportSlice as S;
+                match &result.transport {
+                    Some(S::Icmpv4(icmpv4)) => {
+                        assert_eq!(&test.transport, &Some(H::Icmpv4(icmpv4.header())));
+                        assert_eq!(icmpv4.payload(), expected_payload);
+                    }
+                    Some(S::Icmpv6(icmpv6)) => {
+                        assert_eq!(&test.transport, &Some(H::Icmpv6(icmpv6.header())));
+                        assert_eq!(icmpv6.payload(), expected_payload);
+                    }
+                    Some(S::Udp(s)) => {
+                        assert_eq!(&test.transport, &Some(H::Udp(s.to_header())));
+                    }
+                    Some(S::Tcp(s)) => {
+                        assert_eq!(&test.transport, &Some(H::Tcp(s.to_header())));
+                    }
+                    None => {
+                        assert_eq!(&test.transport, &None);
+                    }
+                }
+            }
+        }
+
+        // setup payload
+        let payload = [1, 2, 3, 4];
+
+        // set length fields in ip headers
+        let test = {
+            let mut test = test_base.clone();
+            test.set_payload_len(payload.len());
+            test
+        };
+
+        // write data
+        let data = test.to_vec(&payload);
+
+        // from_ethernet
+        if test.link.is_some() {
+            let result = SlicedPacket::from_ethernet(&data).unwrap();
+            assert_test_result(&test, &payload, &result);
+        }
+        // from_ether_type (vlan at start)
+        if test.link.is_none() && test.vlan.is_some() {
+            for ether_type in VLAN_ETHER_TYPES {
+                let result = SlicedPacket::from_ether_type(ether_type, &data).unwrap();
+                assert_eq!(
+                    result.link,
+                    Some(LinkSlice::EtherPayload(EtherPayloadSlice {
+                        ether_type,
+                        payload: &data
+                    }))
+                );
+                assert_test_result(&test, &payload, &result);
+            }
+        }
+        // from_ether_type (ip at start)
+        if test.link.is_none() && test.vlan.is_none() {
+            if let Some(ip) = &test.net {
+                let ether_type = match ip {
+                    NetHeaders::Ipv4(_, _) => ether_type::IPV4,
+                    NetHeaders::Ipv6(_, _) => ether_type::IPV6,
+                };
+                let result = SlicedPacket::from_ether_type(ether_type, &data).unwrap();
+                assert_eq!(
+                    result.link,
+                    Some(LinkSlice::EtherPayload(EtherPayloadSlice {
+                        ether_type,
+                        payload: &data
+                    }))
+                );
+                assert_test_result(&test, &payload, &result);
+            }
+        }
+        // from_ip_slice
+        if test.link.is_none() && test.vlan.is_none() && test.net.is_some() {
+            let result = SlicedPacket::from_ip(&data).unwrap();
+            assert_test_result(&test, &payload, &result);
+        }
+    }
+
+    /// Check that the given errors get triggered if presented with the given
+    /// data.
+    fn from_slice_assert_err(test: &TestPacket, data: &[u8], err: SliceError) {
+        // from_ethernet_slice
+        if let Some(ref header) = test.link {
+            match header {
+                LinkHeader::Ethernet2(_) => {
+                    assert_eq!(err.clone(), SlicedPacket::from_ethernet(&data).unwrap_err())
+                }
+                LinkHeader::LinuxSll(_) => assert_eq!(
+                    err.clone(),
+                    SlicedPacket::from_linux_sll(&data).unwrap_err()
+                ),
+            }
+        }
+        // from_ether_type (vlan at start)
+        if test.link.is_none() && test.vlan.is_some() {
+            for ether_type in VLAN_ETHER_TYPES {
+                assert_eq!(
+                    err.clone(),
+                    SlicedPacket::from_ether_type(ether_type, &data).unwrap_err()
+                );
+            }
+        }
+        // from_ether_type (ip at start)
+        if test.link.is_none() && test.vlan.is_none() {
+            if let Some(ip) = &test.net {
+                let err = SlicedPacket::from_ether_type(
+                    match ip {
+                        NetHeaders::Ipv4(_, _) => ether_type::IPV4,
+                        NetHeaders::Ipv6(_, _) => ether_type::IPV6,
+                    },
+                    &data,
+                )
+                .unwrap_err();
+                assert_eq!(err, err.clone());
+            }
+        }
+        // from_ip_slice
+        if test.link.is_none() && test.vlan.is_none() && test.net.is_some() {
+            assert_eq!(err, SlicedPacket::from_ip(&data).unwrap_err());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn payload_ether_type(
+            ref eth in ethernet_2_unknown(),
+            ref linux_sll in linux_sll_any(),
+            ref vlan_outer in vlan_single_unknown(),
+            ref vlan_inner in vlan_single_unknown(),
+            ref ipv4 in ipv4_unknown(),
+            ref udp in udp_any(),
+        ) {
+            use IpHeaders::*;
+            use alloc::vec::Vec;
+
+            // empty
+            {
+                let s = SlicedPacket{
+                    link: None,
+                    vlan: None,
+                    net: None,
+                    transport: None,
+                };
+                assert_eq!(None, s.payload_ether_type());
+            }
+
+            // only linux sll
+            {
+                let mut serialized = Vec::with_capacity(linux_sll.header_len());
+                eth.write(&mut serialized).unwrap();
+                let ether_type = match linux_sll.protocol_type {
+                    LinuxSllProtocolType::EtherType(EtherType(v)) | LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType(v)) => Some(EtherType(v)),
+                    _ => None,
+                };
+                if let Ok(s) = SlicedPacket::from_linux_sll(&serialized) {
+                    assert_eq!(
+                        ether_type,
+                        s.payload_ether_type()
+                    );
+                }
+            }
+
+            // only ethernet
+            {
+                let mut serialized = Vec::with_capacity(eth.header_len());
+                eth.write(&mut serialized).unwrap();
+                assert_eq!(
+                    Some(eth.ether_type),
+                    SlicedPacket::from_ethernet(&serialized)
+                        .unwrap()
+                        .payload_ether_type()
+                );
+            }
+
+            // with single vlan
+            {
+                let mut eth_mod = eth.clone();
+                eth_mod.ether_type = ether_type::VLAN_TAGGED_FRAME;
+
+                let mut serialized = Vec::with_capacity(
+                    eth_mod.header_len() +
+                    vlan_outer.header_len()
+                );
+                eth_mod.write(&mut serialized).unwrap();
+                vlan_outer.write(&mut serialized).unwrap();
+                assert_eq!(
+                    Some(vlan_outer.ether_type),
+                    SlicedPacket::from_ethernet(&serialized)
+                        .unwrap()
+                        .payload_ether_type()
+                );
+            }
+
+            // with double vlan
+            {
+                let mut eth_mod = eth.clone();
+                eth_mod.ether_type = ether_type::VLAN_TAGGED_FRAME;
+
+                let mut vlan_outer_mod = vlan_outer.clone();
+                vlan_outer_mod.ether_type = ether_type::VLAN_TAGGED_FRAME;
+
+                let mut serialized = Vec::with_capacity(
+                    eth_mod.header_len() +
+                    vlan_outer_mod.header_len() +
+                    vlan_inner.header_len()
+                );
+                eth_mod.write(&mut serialized).unwrap();
+                vlan_outer_mod.write(&mut serialized).unwrap();
+                vlan_inner.write(&mut serialized).unwrap();
+                assert_eq!(
+                    Some(vlan_inner.ether_type),
+                    SlicedPacket::from_ethernet(&serialized)
+                        .unwrap()
+                        .payload_ether_type()
+                );
+            }
+
+            // with ip
+            {
+                let builder = PacketBuilder::ethernet2(eth.source, eth.destination)
+                    .ip(Ipv4(ipv4.clone(), Default::default()));
+
+                let mut serialized = Vec::with_capacity(builder.size(0));
+                builder.write(&mut serialized, ipv4.protocol, &[]).unwrap();
+
+                assert_eq!(
+                    None,
+                    SlicedPacket::from_ethernet(&serialized)
+                        .unwrap()
+                        .payload_ether_type()
+                );
+            }
+
+            // with transport
+            {
+                let builder = PacketBuilder::ethernet2(eth.source, eth.destination)
+                    .ip(Ipv4(ipv4.clone(), Default::default()))
+                    .udp(udp.source_port, udp.destination_port);
+                let mut serialized = Vec::with_capacity(builder.size(0));
+                builder.write(&mut serialized, &[]).unwrap();
+
+                assert_eq!(
+                    None,
+                    SlicedPacket::from_ethernet(&serialized)
+                        .unwrap()
+                        .payload_ether_type()
+                );
+            }
+        }
+    }
+}
diff --git a/src/sliced_packet_cursor.rs b/src/sliced_packet_cursor.rs
new file mode 100644
index 0000000..fb1a364
--- /dev/null
+++ b/src/sliced_packet_cursor.rs
@@ -0,0 +1,354 @@
+use crate::*;
+
+/// 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> {
+        SlicedPacketCursor {
+            slice,
+            offset: 0,
+            len_source: LenSource::Slice,
+            result: SlicedPacket {
+                link: None,
+                vlan: None,
+                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<SlicedPacket<'a>, err::packet::SliceError> {
+        use err::packet::SliceError::*;
+        use ether_type::*;
+        use LinkSlice::*;
+
+        let result = Ethernet2Slice::from_slice_without_fcs(self.slice)
+            .map_err(|err| Len(err.add_offset(self.offset)))?;
+
+        //cache the ether_type for later
+        let ether_type = result.ether_type();
+
+        //set the new data
+        self.move_by(result.header_len());
+        self.result.link = Some(Ethernet2(result));
+
+        //continue parsing (if required)
+        match ether_type {
+            IPV4 => self.slice_ipv4(),
+            IPV6 => self.slice_ipv6(),
+            VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => self.slice_vlan(),
+            _ => Ok(self.result),
+        }
+    }
+
+    pub fn slice_linux_sll(mut self) -> Result<SlicedPacket<'a>, err::packet::SliceError> {
+        use err::packet::SliceError::*;
+
+        let result = LinuxSllSlice::from_slice(self.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)
+            }
+        })?;
+
+        //cache the protocol type for later
+        let protocol_type = result.protocol_type();
+
+        //set the new data
+        self.move_by(result.header_len());
+        self.result.link = Some(LinkSlice::LinuxSll(result));
+
+        //continue parsing (if required)
+        match protocol_type {
+            LinuxSllProtocolType::EtherType(EtherType::IPV4) => self.slice_ipv4(),
+            LinuxSllProtocolType::EtherType(EtherType::IPV6) => self.slice_ipv6(),
+            _ => Ok(self.result),
+        }
+    }
+
+    pub fn slice_vlan(mut self) -> Result<SlicedPacket<'a>, 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 {
+                    IPV4 => self.slice_ipv4(),
+                    IPV6 => self.slice_ipv6(),
+                    _ => Ok(self.result),
+                }
+            }
+            IPV4 => self.slice_ipv4(),
+            IPV6 => self.slice_ipv6(),
+            _ => Ok(self.result),
+        }
+    }
+
+    pub fn slice_ip(mut self) -> Result<SlicedPacket<'a>, 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| {
+            use err::ip::SliceError as I;
+            match err {
+                I::Len(mut err) => {
+                    err.layer_start_offset += self.offset;
+                    Len(err)
+                }
+                I::IpHeaders(err) => match err {
+                    err::ip::HeadersError::Ip(err) => Ip(err),
+                    err::ip::HeadersError::Ipv4Ext(err) => Ipv4Exts(err),
+                    err::ip::HeadersError::Ipv6Ext(err) => Ipv6Exts(err),
+                },
+            }
+        })?;
+
+        // safe data needed
+        let payload = ip.payload().clone();
+
+        // set the new data
+        self.offset += unsafe {
+            // 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
+        };
+        self.len_source = payload.len_source;
+        self.slice = payload.payload;
+        self.result.net = Some(ip.into());
+
+        // continue to the lower layers
+        if payload.fragmented {
+            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| {
+                    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),
+                _ => Ok(self.result),
+            }
+        }
+    }
+
+    pub fn slice_ipv4(mut self) -> Result<SlicedPacket<'a>, err::packet::SliceError> {
+        use err::packet::SliceError::*;
+
+        // slice ipv4 header & extension headers
+        let ipv4 = Ipv4Slice::from_slice(self.slice).map_err(|err| {
+            use err::ipv4::SliceError as I;
+            match err {
+                I::Len(mut err) => {
+                    err.layer_start_offset += self.offset;
+                    Len(err)
+                }
+                I::Header(err) => Ipv4(err),
+                I::Exts(err) => Ipv4Exts(err),
+            }
+        })?;
+
+        // safe data needed in following steps
+        let payload = ipv4.payload().clone();
+
+        // set the new data
+        self.offset += unsafe {
+            // 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
+        };
+        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| {
+                    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),
+                _ => Ok(self.result),
+            }
+        }
+    }
+
+    pub fn slice_ipv6(mut self) -> Result<SlicedPacket<'a>, err::packet::SliceError> {
+        use err::packet::SliceError::*;
+
+        let ipv6 = Ipv6Slice::from_slice(self.slice).map_err(|err| {
+            use err::ipv6::SliceError as I;
+            match err {
+                I::Len(mut err) => {
+                    err.layer_start_offset += self.offset;
+                    Len(err)
+                }
+                I::Header(err) => Ipv6(err),
+                I::Exts(err) => Ipv6Exts(err),
+            }
+        })?;
+
+        // safe data needed in following steps
+        let payload = ipv6.payload().clone();
+
+        // set the new data
+        self.offset += unsafe {
+            // 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
+        };
+        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
+        // is not fragmented
+        if payload.fragmented {
+            Ok(self.result)
+        } 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| {
+                    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),
+                _ => Ok(self.result),
+            }
+        }
+    }
+
+    pub fn slice_icmp4(mut self) -> Result<SlicedPacket<'a>, err::LenError> {
+        use crate::TransportSlice::*;
+
+        let result = Icmpv4Slice::from_slice(self.slice).map_err(|mut err| {
+            err.layer_start_offset += self.offset;
+            if LenSource::Slice == err.len_source {
+                err.len_source = self.len_source;
+            }
+            err
+        })?;
+
+        //set the new data
+        self.move_by(result.slice().len());
+        self.result.transport = Some(Icmpv4(result.clone()));
+
+        Ok(self.result)
+    }
+
+    pub fn slice_icmp6(mut self) -> Result<SlicedPacket<'a>, err::LenError> {
+        use crate::TransportSlice::*;
+
+        let result = Icmpv6Slice::from_slice(self.slice).map_err(|mut err| {
+            err.layer_start_offset += self.offset;
+            if LenSource::Slice == err.len_source {
+                err.len_source = self.len_source;
+            }
+            err
+        })?;
+
+        //set the new data
+        self.move_by(result.slice().len());
+        self.result.transport = Some(Icmpv6(result.clone()));
+
+        //done
+        Ok(self.result)
+    }
+
+    pub fn slice_udp(mut self) -> Result<SlicedPacket<'a>, err::LenError> {
+        use crate::TransportSlice::*;
+
+        let result = UdpSlice::from_slice(self.slice).map_err(|mut err| {
+            err.layer_start_offset += self.offset;
+            if LenSource::Slice == err.len_source {
+                err.len_source = self.len_source;
+            }
+            err
+        })?;
+
+        //set the new data
+        self.move_by(result.slice().len());
+        self.result.transport = Some(Udp(result));
+
+        // done
+        Ok(self.result)
+    }
+
+    pub fn slice_tcp(mut self) -> Result<SlicedPacket<'a>, err::tcp::HeaderSliceError> {
+        use crate::TransportSlice::*;
+
+        let result = TcpSlice::from_slice(self.slice).map_err(|mut err| {
+            use err::tcp::HeaderSliceError::Len;
+            if let Len(err) = &mut err {
+                err.layer_start_offset += self.offset;
+                if LenSource::Slice == err.len_source {
+                    err.len_source = self.len_source;
+                }
+            }
+            err
+        })?;
+
+        //set the new data
+        self.move_by(result.slice().len());
+        self.result.transport = Some(Tcp(result));
+
+        // done
+        Ok(self.result)
+    }
+}
diff --git a/src/test_gens/mod.rs b/src/test_gens/mod.rs
new file mode 100644
index 0000000..c677a35
--- /dev/null
+++ b/src/test_gens/mod.rs
@@ -0,0 +1,724 @@
+use crate::*;
+use proptest::prelude::*;
+use proptest::*;
+
+pub fn vlan_ethertype_any() -> impl Strategy<Value = EtherType> {
+    prop_oneof![
+        Just(ether_type::VLAN_TAGGED_FRAME),
+        Just(ether_type::PROVIDER_BRIDGING),
+        Just(ether_type::VLAN_DOUBLE_TAGGED_FRAME),
+    ]
+}
+
+prop_compose! {
+    pub fn ether_type_any()
+        (value in any::<u16>())
+        -> EtherType
+    {
+        EtherType(value)
+    }
+}
+
+prop_compose! {
+    pub fn vlan_id_any()
+        (value in 0..=0b0000_1111_1111_1111u16)
+        -> VlanId
+    {
+        VlanId::try_new(value).unwrap()
+    }
+}
+
+prop_compose! {
+    pub fn vlan_pcp_any()
+        (value in 0..=0b0000_0111u8)
+        -> VlanPcp
+    {
+        VlanPcp::try_new(value).unwrap()
+    }
+}
+
+prop_compose! {
+    pub fn vlan_single_unknown()(
+        pcp in vlan_pcp_any(),
+        drop_eligible_indicator in any::<bool>(),
+        vlan_id in vlan_id_any(),
+        ether_type in ether_type_any().prop_filter("ether_type must be unknown",
+            |v| !ETHERNET_KNOWN_ETHER_TYPES.iter().any(|&x| v == &x)))
+        -> SingleVlanHeader
+    {
+        SingleVlanHeader {
+            pcp,
+            drop_eligible_indicator,
+            vlan_id,
+            ether_type,
+        }
+    }
+}
+
+prop_compose! {
+    pub fn ipv6_flow_label_any()
+        (value in 0u32..=0b1111_11111111_11111111u32)
+        -> Ipv6FlowLabel
+    {
+        Ipv6FlowLabel::try_new(value).unwrap()
+    }
+}
+
+prop_compose! {
+    pub fn ip_number_any()
+        (value in any::<u8>())
+        -> IpNumber
+    {
+        IpNumber(value)
+    }
+}
+
+prop_compose! {
+    pub fn ethernet_2_with(ether_type: EtherType)(
+        source in prop::array::uniform6(any::<u8>()),
+        dest in prop::array::uniform6(any::<u8>()),
+        ether_type in proptest::strategy::Just(ether_type))
+        -> Ethernet2Header
+    {
+        Ethernet2Header {
+            source: source,
+            destination: dest,
+            ether_type: ether_type
+        }
+    }
+}
+
+prop_compose! {
+    pub fn ethernet_2_any()
+        (ether_type in ether_type_any())
+        (result in ethernet_2_with(ether_type))
+        -> Ethernet2Header
+    {
+        result
+    }
+}
+
+prop_compose! {
+    pub fn linux_sll_packet_type_any()
+        (value in 0..=LinuxSllPacketType::MAX_VAL)
+        -> LinuxSllPacketType
+    {
+        LinuxSllPacketType::try_from(value).unwrap()
+    }
+}
+
+prop_compose! {
+    pub fn linux_sll_arphrd()
+        (index in 0..=(LinuxSllProtocolType::SUPPORTED_ARPHWD.len()-1))
+        -> ArpHardwareId
+    {
+        LinuxSllProtocolType::SUPPORTED_ARPHWD[index]
+    }
+}
+
+prop_compose! {
+    pub fn linux_sll_sender_adress_any()
+        (mut sender_address in prop::collection::vec(any::<u8>(), 0..8))
+        -> (u16, [u8; 8])
+    {
+        let size = sender_address.len().try_into().unwrap();
+        sender_address.resize(8, 0);
+        let mut v: [u8; 8] = [0u8;8];
+        v.copy_from_slice(&sender_address);
+
+        (size, v)
+    }
+}
+
+prop_compose! {
+    pub fn linux_sll_any()
+        (packet_type in linux_sll_packet_type_any(),
+        arp_hrd_type in linux_sll_arphrd(),
+        (sender_address_valid_length, sender_address) in linux_sll_sender_adress_any(),
+        protocol_num in any::<u16>()
+    )
+        -> LinuxSllHeader
+    {
+        LinuxSllHeader {
+            packet_type,
+            arp_hrd_type,
+            sender_address_valid_length,
+            sender_address,
+            protocol_type: LinuxSllProtocolType::try_from((arp_hrd_type, protocol_num)).unwrap()
+        }
+    }
+}
+
+pub static ETHERNET_KNOWN_ETHER_TYPES: &'static [EtherType] = &[
+    ether_type::IPV4,
+    ether_type::IPV6,
+    ether_type::VLAN_TAGGED_FRAME,
+    ether_type::PROVIDER_BRIDGING,
+    ether_type::VLAN_DOUBLE_TAGGED_FRAME,
+];
+
+prop_compose! {
+    pub fn ethernet_2_unknown()(
+        source in prop::array::uniform6(any::<u8>()),
+        dest in prop::array::uniform6(any::<u8>()),
+        ether_type in ether_type_any().prop_filter("ether_type must be unknown",
+            |v| !ETHERNET_KNOWN_ETHER_TYPES.iter().any(|&x| v == &x)))
+        -> Ethernet2Header
+    {
+        Ethernet2Header {
+            source: source,
+            destination: dest,
+            ether_type: ether_type
+        }
+    }
+}
+
+prop_compose! {
+    pub fn vlan_single_with(ether_type: EtherType)(
+        pcp in vlan_pcp_any(),
+        drop_eligible_indicator in any::<bool>(),
+        vlan_id in vlan_id_any(),
+        ether_type in proptest::strategy::Just(ether_type))
+        -> SingleVlanHeader
+    {
+        SingleVlanHeader {
+            pcp,
+            drop_eligible_indicator,
+            vlan_id,
+            ether_type,
+        }
+    }
+}
+
+prop_compose! {
+    pub fn vlan_single_any()
+        (ether_type in ether_type_any())
+        (result in vlan_single_with(ether_type))
+        -> SingleVlanHeader
+    {
+        result
+    }
+}
+
+prop_compose! {
+    pub fn vlan_double_any()
+        (ether_type in ether_type_any())
+        (result in vlan_double_with(ether_type))
+        -> DoubleVlanHeader
+    {
+        result
+    }
+}
+
+prop_compose! {
+    pub fn vlan_double_with(ether_type: EtherType)(
+        outer_ethertype in vlan_ethertype_any(),
+        inner_ethertype in proptest::strategy::Just(ether_type)
+    )(
+        outer in vlan_single_with(outer_ethertype),
+        inner in vlan_single_with(inner_ethertype)
+    ) -> DoubleVlanHeader {
+        DoubleVlanHeader {
+            outer,
+            inner
+        }
+    }
+}
+
+prop_compose! {
+    pub fn ipv4_options_any()
+    (
+        len_div_4 in 0u8..10,
+        options_part0 in prop::array::uniform32(any::<u8>()),
+        options_part1 in prop::array::uniform8(any::<u8>())
+    ) -> Ipv4Options
+    {
+        let mut options: [u8;40] = [0;40];
+        //copy together 40 bytes of random data (the limit for static arrays in proptest 32,
+        //so a 32 & 8 byte array get combined here)
+        let len = usize::from(len_div_4)*4;
+        if len > 0 {
+            let sub_len = std::cmp::min(len,32);
+            options[..sub_len].copy_from_slice(&options_part0[..sub_len]);
+        }
+        if len > 32 {
+            let sub_len = len - 32;
+            options[32..len].copy_from_slice(&options_part1[..sub_len]);
+        }
+
+        //set the options
+        (&options[..len]).try_into().unwrap()
+    }
+}
+
+prop_compose! {
+    pub fn ipv4_with(protocol: IpNumber)
+    (
+        protocol in proptest::strategy::Just(protocol),
+        options in ipv4_options_any()
+    )(
+        source in prop::array::uniform4(any::<u8>()),
+        destination in prop::array::uniform4(any::<u8>()),
+        dscp in 0u8..=0b0011_1111,
+        ecn in 0u8..=0b0000_0011,
+        identification in any::<u16>(),
+        time_to_live in any::<u8>(),
+        dont_fragment in any::<bool>(),
+        more_fragments in any::<bool>(),
+        fragment_offset in prop::bits::u16::between(0, 13),
+        header_checksum in any::<u16>(),
+        total_len in ((Ipv4Header::MIN_LEN + usize::from(options.len())) as u16)..core::u16::MAX,
+        protocol in proptest::strategy::Just(protocol),
+        options in proptest::strategy::Just(options)
+    ) -> Ipv4Header
+    {
+        Ipv4Header{
+            dscp: dscp.try_into().unwrap(),
+            ecn: ecn.try_into().unwrap(),
+            total_len,
+            identification,
+            dont_fragment,
+            more_fragments,
+            fragment_offset: fragment_offset.try_into().unwrap(),
+            time_to_live,
+            protocol,
+            header_checksum,
+            source,
+            destination,
+            options
+        }
+    }
+}
+prop_compose! {
+    pub fn ipv4_any()
+               (protocol in ip_number_any())
+               (result in ipv4_with(protocol))
+               -> Ipv4Header
+    {
+        result
+    }
+}
+
+static IPV4_KNOWN_PROTOCOLS: &[IpNumber] = &[
+    ip_number::ICMP,
+    ip_number::UDP,
+    ip_number::TCP,
+    ip_number::AUTH,
+    ip_number::IPV6_ICMP,
+];
+
+prop_compose! {
+    pub fn ipv4_unknown()
+        (protocol in ip_number_any().prop_filter("protocol must be unknown",
+            |v| !IPV4_KNOWN_PROTOCOLS.iter().any(|&x| v == &x))
+        )
+        (header in ipv4_with(protocol)
+    ) -> Ipv4Header
+    {
+        header
+    }
+}
+
+prop_compose! {
+    pub fn ipv4_extensions_with(next_header: IpNumber)
+    (
+        has_auth in any::<bool>(),
+        auth in ip_auth_with(next_header)
+    ) -> Ipv4Extensions
+    {
+        if has_auth {
+            Ipv4Extensions{
+                auth: Some(auth),
+            }
+        } else {
+            Ipv4Extensions{
+                auth: None,
+            }
+        }
+    }
+}
+
+prop_compose! {
+    pub fn ipv4_extensions_any()
+               (protocol in ip_number_any())
+               (result in ipv4_extensions_with(protocol))
+               -> Ipv4Extensions
+    {
+        result
+    }
+}
+
+prop_compose! {
+    pub fn ipv4_extensions_unknown()
+        (
+            next_header in ip_number_any().prop_filter(
+                "next_header must be unknown",
+                |v| !IPV4_KNOWN_PROTOCOLS.iter().any(|&x| v == &x)
+            )
+        ) (
+            result in ipv4_extensions_with(next_header)
+        ) -> Ipv4Extensions
+    {
+        result
+    }
+}
+
+prop_compose! {
+    pub fn ipv6_with(next_header: IpNumber)
+    (
+        source in prop::array::uniform16(any::<u8>()),
+        dest in prop::array::uniform16(any::<u8>()),
+        traffic_class in any::<u8>(),
+        flow_label in ipv6_flow_label_any(),
+        payload_length in any::<u16>(),
+        hop_limit in any::<u8>(),
+        next_header in proptest::strategy::Just(next_header)
+    ) -> Ipv6Header
+    {
+        Ipv6Header {
+            traffic_class: traffic_class,
+            flow_label: flow_label,
+            payload_length: payload_length,
+            next_header: next_header,
+            hop_limit: hop_limit,
+            source: source,
+            destination: dest
+        }
+    }
+}
+
+prop_compose! {
+    pub fn ipv6_any()
+        (next_header in ip_number_any())
+        (result in ipv6_with(next_header)
+    ) -> Ipv6Header
+    {
+        result
+    }
+}
+
+static IPV6_KNOWN_NEXT_HEADERS: &[IpNumber] = &[
+    ip_number::ICMP,
+    ip_number::UDP,
+    ip_number::TCP,
+    ip_number::IPV6_HOP_BY_HOP,
+    ip_number::IPV6_ICMP,
+    ip_number::IPV6_ROUTE,
+    ip_number::IPV6_FRAG,
+    ip_number::AUTH,
+    ip_number::IPV6_DEST_OPTIONS,
+    ip_number::MOBILITY,
+    ip_number::HIP,
+    ip_number::SHIM6,
+    // currently not supported:
+    // - EncapsulatingSecurityPayload
+    // - ExperimentalAndTesting0
+    // - ExperimentalAndTesting1
+];
+
+prop_compose! {
+    pub fn ipv6_unknown()(
+        source in prop::array::uniform16(any::<u8>()),
+        destination in prop::array::uniform16(any::<u8>()),
+        traffic_class in any::<u8>(),
+        flow_label in ipv6_flow_label_any(),
+        payload_length in any::<u16>(),
+        hop_limit in any::<u8>(),
+        next_header in ip_number_any().prop_filter("next_header must be unknown",
+            |v| !IPV6_KNOWN_NEXT_HEADERS.iter().any(|&x| v == &x))
+    ) -> Ipv6Header
+    {
+        Ipv6Header {
+            traffic_class,
+            flow_label,
+            payload_length,
+            next_header,
+            hop_limit,
+            source,
+            destination,
+        }
+    }
+}
+
+prop_compose! {
+    pub fn ipv6_raw_ext_with(
+        next_header: IpNumber,
+        len: u8
+    ) (
+        next_header in proptest::strategy::Just(next_header),
+        payload in proptest::collection::vec(any::<u8>(), (len as usize)*8 + 6)
+    ) -> Ipv6RawExtHeader
+    {
+        Ipv6RawExtHeader::new_raw(
+            next_header,
+            &payload[..]
+        ).unwrap()
+    }
+}
+
+prop_compose! {
+    pub fn ipv6_raw_ext_any()
+        (
+            next_header in ip_number_any(),
+            len in any::<u8>()
+        ) (
+            result in ipv6_raw_ext_with(next_header, len)
+    ) -> Ipv6RawExtHeader
+    {
+        result
+    }
+}
+
+prop_compose! {
+    pub fn ipv6_extensions_with(next_header: IpNumber)
+    (
+        has_hop_by_hop_options in any::<bool>(),
+        hop_by_hop_options in ipv6_raw_ext_any(),
+        has_destination_options in any::<bool>(),
+        destination_options in ipv6_raw_ext_any(),
+        has_routing in any::<bool>(),
+        routing in ipv6_raw_ext_any(),
+        has_fragment in any::<bool>(),
+        fragment in ipv6_fragment_any(),
+        has_auth in any::<bool>(),
+        auth in ip_auth_with(next_header),
+        has_final_destination_options in any::<bool>(),
+        final_destination_options in ipv6_raw_ext_any()
+    ) -> Ipv6Extensions
+    {
+        let mut result = Ipv6Extensions {
+            hop_by_hop_options: if has_hop_by_hop_options {
+                Some(hop_by_hop_options)
+            } else {
+                None
+            },
+            destination_options: if has_destination_options {
+                Some(destination_options)
+            } else {
+                None
+            },
+            routing: if has_routing {
+                Some(
+                    Ipv6RoutingExtensions{
+                        routing,
+                        final_destination_options: if has_final_destination_options {
+                            Some(final_destination_options)
+                        } else {
+                            None
+                        }
+                    }
+                )
+            } else {
+                None
+            },
+            fragment: if has_fragment {
+                Some(fragment)
+            } else {
+                None
+            },
+            auth: if has_auth {
+                Some(auth)
+            } else {
+                None
+            },
+        };
+        result.set_next_headers(next_header);
+        result
+    }
+}
+
+prop_compose! {
+    pub fn ipv6_extensions_any()
+        (
+            next_header in ip_number_any()
+        ) (
+            result in ipv6_extensions_with(next_header)
+    ) -> Ipv6Extensions
+    {
+        result
+    }
+}
+
+prop_compose! {
+    pub fn ipv6_extensions_unknown()
+        (
+            next_header in ip_number_any().prop_filter(
+                "next_header must be unknown",
+                |v| !IPV6_KNOWN_NEXT_HEADERS.iter().any(|&x| v == &x)
+            )
+        ) (
+            result in ipv6_extensions_with(next_header)
+        ) -> Ipv6Extensions
+    {
+        result
+    }
+}
+
+prop_compose! {
+    pub fn ipv6_fragment_with(
+        next_header: IpNumber
+    ) (
+        next_header in proptest::strategy::Just(next_header),
+        fragment_offset in 0u16..=0b0001_1111_1111_1111u16,
+        more_fragments in any::<bool>(),
+        identification in any::<u32>(),
+    ) -> Ipv6FragmentHeader
+    {
+        Ipv6FragmentHeader::new(
+            next_header,
+            fragment_offset.try_into().unwrap(),
+            more_fragments,
+            identification
+        )
+    }
+}
+
+prop_compose! {
+    pub fn ipv6_fragment_any()
+        (next_header in ip_number_any())
+        (result in ipv6_fragment_with(next_header)
+    ) -> Ipv6FragmentHeader
+    {
+        result
+    }
+}
+
+prop_compose! {
+    pub fn ip_auth_with(
+        next_header: IpNumber
+    ) (
+        next_header in proptest::strategy::Just(next_header),
+        len in 1..0xffu8
+    ) (
+        next_header in proptest::strategy::Just(next_header),
+        spi in any::<u32>(),
+        sequence_number in any::<u32>(),
+        icv in proptest::collection::vec(any::<u8>(), (len as usize)*4)
+    ) -> IpAuthHeader {
+        IpAuthHeader::new(
+            next_header,
+            spi,
+            sequence_number,
+            &icv
+        ).unwrap()
+    }
+}
+
+prop_compose! {
+    pub fn ip_auth_any() (
+        next_header in ip_number_any()
+    ) (
+        header in ip_auth_with(next_header)
+    ) -> IpAuthHeader {
+        header
+    }
+}
+
+prop_compose! {
+    pub fn udp_any()(
+            source_port in any::<u16>(),
+            destination_port in any::<u16>(),
+            length in any::<u16>(),
+            checksum in any::<u16>())
+        -> UdpHeader
+    {
+        UdpHeader {
+            source_port: source_port,
+            destination_port: destination_port,
+            length: length,
+            checksum: checksum
+        }
+    }
+}
+
+prop_compose! {
+    pub fn tcp_any()
+        (data_offset in TcpHeader::MIN_DATA_OFFSET..(TcpHeader::MAX_DATA_OFFSET + 1))
+        (
+            source_port in any::<u16>(),
+            destination_port in any::<u16>(),
+            sequence_number in any::<u32>(),
+            acknowledgment_number in any::<u32>(),
+            ns in any::<bool>(),
+            fin in any::<bool>(),
+            syn in any::<bool>(),
+            rst in any::<bool>(),
+            psh in any::<bool>(),
+            ack in any::<bool>(),
+            ece in any::<bool>(),
+            urg in any::<bool>(),
+            cwr  in any::<bool>(),
+            window_size in any::<u16>(),
+            checksum in any::<u16>(),
+            urgent_pointer in any::<u16>(),
+            options in proptest::collection::vec(any::<u8>(), ((data_offset - 5) as usize)*4))
+        -> TcpHeader
+    {
+        let mut result = TcpHeader::new(source_port, destination_port, sequence_number, window_size);
+        result.acknowledgment_number = acknowledgment_number;
+        result.ns = ns;
+        result.fin = fin;
+        result.syn = syn;
+        result.rst = rst;
+        result.psh = psh;
+        result.ack = ack;
+        result.ece = ece;
+        result.urg = urg;
+        result.cwr = cwr;
+        result.checksum = checksum;
+        result.urgent_pointer = urgent_pointer;
+        result.set_options_raw(&options[..]).unwrap();
+        result
+    }
+}
+
+prop_compose! {
+    pub fn tcp_options_any()
+    (data_offset in TcpHeader::MIN_DATA_OFFSET..(TcpHeader::MAX_DATA_OFFSET + 1))
+    (
+        options in proptest::collection::vec(any::<u8>(), ((data_offset - 5) as usize)*4)
+    ) -> TcpOptions
+    {
+        TcpOptions::try_from_slice(&options).unwrap()
+    }
+}
+
+prop_compose! {
+    pub fn icmpv4_type_any()
+        (
+            bytes in any::<[u8;20]>(),
+        ) -> Icmpv4Type
+    {
+        Icmpv4Header::from_slice(&bytes).unwrap().0.icmp_type
+    }
+}
+
+prop_compose! {
+    pub fn icmpv4_header_any()
+        (
+            bytes in any::<[u8;20]>(),
+        ) -> Icmpv4Header
+    {
+        Icmpv4Header::from_slice(&bytes).unwrap().0
+    }
+}
+
+prop_compose! {
+    pub fn icmpv6_type_any()
+        (
+            bytes in any::<[u8;8]>(),
+        ) -> Icmpv6Type
+    {
+        Icmpv6Header::from_slice(&bytes).unwrap().0.icmp_type
+    }
+}
+
+prop_compose! {
+    pub fn icmpv6_header_any()
+        (
+            bytes in any::<[u8;8]>(),
+        ) -> Icmpv6Header
+    {
+        Icmpv6Header::from_slice(&bytes).unwrap().0
+    }
+}
diff --git a/src/test_packet.rs b/src/test_packet.rs
new file mode 100644
index 0000000..13691d6
--- /dev/null
+++ b/src/test_packet.rs
@@ -0,0 +1,130 @@
+use crate::*;
+use alloc::vec::Vec;
+
+#[derive(Clone)]
+pub(crate) struct TestPacket {
+    pub link: Option<LinkHeader>,
+    pub vlan: Option<VlanHeader>,
+    pub net: Option<NetHeaders>,
+    pub transport: Option<TransportHeader>,
+}
+
+impl TestPacket {
+    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.net.as_ref().map_or(0, |x| x.header_len())
+            + self.transport.as_ref().map_or(0, |x| x.header_len())
+            + payload.len()
+    }
+
+    pub fn to_vec(&self, payload: &[u8]) -> Vec<u8> {
+        let mut result = Vec::with_capacity(self.len(payload));
+        if let Some(link) = &self.link {
+            link.write(&mut result).unwrap();
+        }
+        if let Some(vlan) = &self.vlan {
+            vlan.write(&mut result).unwrap();
+        }
+        if let Some(ip) = &self.net {
+            match ip {
+                NetHeaders::Ipv4(ipv4, exts) => {
+                    ipv4.write_raw(&mut result).unwrap();
+                    exts.write(&mut result, ipv4.protocol).unwrap();
+                }
+                NetHeaders::Ipv6(ipv6, exts) => {
+                    ipv6.write(&mut result).unwrap();
+                    exts.write(&mut result, ipv6.next_header).unwrap();
+                }
+            }
+        }
+        if let Some(transport) = &self.transport {
+            transport.write(&mut result).unwrap();
+        }
+        result.extend_from_slice(payload);
+        result
+    }
+
+    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;
+                }
+                Double(double) => {
+                    double.inner.ether_type = ether_type;
+                }
+            }
+        } else 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)
+                }
+            }
+        }
+    }
+
+    pub fn set_payload_len(&mut self, payload_len: usize) {
+        use NetHeaders::*;
+        match &mut self.net {
+            None => {}
+            Some(Ipv4(ref mut header, ref mut exts)) => {
+                header
+                    .set_payload_len(
+                        exts.header_len()
+                            + self.transport.as_ref().map_or(0, |t| t.header_len())
+                            + payload_len,
+                    )
+                    .unwrap();
+            }
+            Some(Ipv6(ref mut header, ref mut exts)) => {
+                header
+                    .set_payload_length(
+                        exts.header_len()
+                            + self.transport.as_ref().map_or(0, |t| t.header_len())
+                            + payload_len,
+                    )
+                    .unwrap();
+            }
+        }
+
+        use TransportHeader::*;
+        match &mut self.transport {
+            None => {}
+            Some(Udp(ref mut udp)) => {
+                udp.length = udp.header_len_u16() + payload_len as u16;
+            }
+            Some(Tcp(_)) => {}
+            Some(Icmpv4(_)) => {}
+            Some(Icmpv6(_)) => {}
+        }
+    }
+
+    /// 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) {
+        use NetHeaders::*;
+        match self.net.as_mut().unwrap() {
+            Ipv4(ref mut header, ref mut exts) => {
+                header
+                    .set_payload_len((exts.header_len() as isize + payload_len_from_ip_on) as usize)
+                    .unwrap();
+            }
+            Ipv6(ref mut header, ref mut exts) => {
+                header
+                    .set_payload_length(
+                        (exts.header_len() as isize + payload_len_from_ip_on) as usize,
+                    )
+                    .unwrap();
+            }
+        }
+    }
+
+    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(),
+            NetHeaders::Ipv6(_, e) => e.is_fragmenting_payload(),
+        })
+    }
+}
diff --git a/src/transport/icmp_echo_header.rs b/src/transport/icmp_echo_header.rs
new file mode 100644
index 0000000..ba139a9
--- /dev/null
+++ b/src/transport/icmp_echo_header.rs
@@ -0,0 +1,96 @@
+/// Echo Request & Response common parts between ICMPv4 and ICMPv6.
+///
+/// # RFC 4443 Description (ICMPv6)
+///
+/// Every node MUST implement an ICMPv6 Echo responder function that
+/// receives Echo Requests and originates corresponding Echo Replies.  A
+/// 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)]
+pub struct IcmpEchoHeader {
+    /// An identifier to aid in matching Echo Replies to Echo Requests. May be zero.
+    pub id: u16,
+    /// A sequence number to aid in matching Echo Replies to Echo Requests. May be zero.
+    pub seq: u16,
+}
+
+impl IcmpEchoHeader {
+    /// Serialized size of an IcmpEchoHeader header in bytes/octets.
+    pub const LEN: usize = 4;
+
+    /// Return the seq + id encoded to the on the wire format.
+    #[inline]
+    pub fn to_bytes(&self) -> [u8; 4] {
+        let id_be = self.id.to_be_bytes();
+        let seq_be = self.seq.to_be_bytes();
+        [id_be[0], id_be[1], seq_be[0], seq_be[1]]
+    }
+
+    /// Decodes the seq + id from the on the wire format.
+    #[inline]
+    pub fn from_bytes(bytes5to8: [u8; 4]) -> IcmpEchoHeader {
+        IcmpEchoHeader {
+            id: u16::from_be_bytes([bytes5to8[0], bytes5to8[1]]),
+            seq: u16::from_be_bytes([bytes5to8[2], bytes5to8[3]]),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::*;
+    use alloc::format;
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn to_bytes(
+            id in any::<u16>(),
+            seq in any::<u16>()
+        ) {
+            let id_bytes = id.to_be_bytes();
+            let seq_bytes = seq.to_be_bytes();
+            assert_eq!(
+                IcmpEchoHeader{ id, seq }.to_bytes(),
+                [
+                    id_bytes[0], id_bytes[1],
+                    seq_bytes[0], seq_bytes[1]
+                ]
+            );
+        }
+
+        #[test]
+        fn from_bytes(
+            bytes in any::<[u8;4]>()
+        ) {
+            assert_eq!(
+                IcmpEchoHeader::from_bytes(bytes),
+                IcmpEchoHeader {
+                    id: u16::from_be_bytes([bytes[0], bytes[1]]),
+                    seq: u16::from_be_bytes([bytes[2], bytes[3]])
+                }
+            );
+        }
+
+        #[test]
+        fn clone_eq(
+            id in any::<u16>(),
+            seq in any::<u16>()
+        ) {
+            let value = IcmpEchoHeader{ id, seq };
+            assert_eq!(value.clone(), value);
+        }
+
+        #[test]
+        fn debug(
+            id in any::<u16>(),
+            seq in any::<u16>()
+        ) {
+            assert_eq!(
+                format!("{:?}", IcmpEchoHeader{ id, seq }),
+                format!("IcmpEchoHeader {{ id: {:?}, seq: {:?} }}", id, seq)
+            );
+        }
+    }
+}
diff --git a/src/transport/icmpv4/dest_unreachable_header.rs b/src/transport/icmpv4/dest_unreachable_header.rs
new file mode 100644
index 0000000..3f5c10d
--- /dev/null
+++ b/src/transport/icmpv4/dest_unreachable_header.rs
@@ -0,0 +1,234 @@
+/// "Destination Unreachable" ICMP header for IPv4 (without the invoking packet).
+///
+/// # Description in RFC 792:
+///
+/// If, according to the information in the gateway's routing tables,
+/// the network specified in the internet destination field of a
+/// datagram is unreachable, e.g., the distance to the network is
+/// infinity, the gateway may send a destination unreachable message
+/// to the internet source host of the datagram.  In addition, in some
+/// networks, the gateway may be able to determine if the internet
+/// destination host is unreachable.  Gateways in these networks may
+/// send destination unreachable messages to the source host when the
+/// destination host is unreachable.
+///
+/// If, in the destination host, the IP module cannot deliver the
+/// datagram  because the indicated protocol module or process port is
+/// not active, the destination host may send a destination
+/// unreachable message to the source host.
+///
+/// Another case is when a datagram must be fragmented to be forwarded
+/// by a gateway yet the Don't Fragment flag is on.  In this case the
+/// gateway must discard the datagram and may return a destination
+/// unreachable message.
+///
+/// Codes 0, 1, 4, and 5 may be received from a gateway.  Codes 2 and
+/// 3 may be received from a host.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum DestUnreachableHeader {
+    /// Network unreachable error.
+    Network,
+    /// Host unreachable error.
+    Host,
+    /// Transport protocol not supported error.
+    Protocol,
+    /// Port unreachable error.
+    Port,
+    /// Fragmentation would be needed but the don't fragment bit is set.
+    FragmentationNeeded { next_hop_mtu: u16 },
+    /// Source Route Failed
+    SourceRouteFailed,
+    /// Destination Network Unknown (from [RFC 1122](https://tools.ietf.org/html/rfc1122))
+    NetworkUnknown,
+    /// Destination Host Unknown (no route to host known) (from [RFC 1122](https://tools.ietf.org/html/rfc1122))
+    HostUnknown,
+    /// Source Host Isolated - obsolete (from [RFC 1122](https://tools.ietf.org/html/rfc1122))
+    Isolated,
+    /// Communication with Destination Network is Administratively Prohibited (from [RFC 1122](https://tools.ietf.org/html/rfc1122))
+    NetworkProhibited,
+    /// Communication with Destination Host is Administratively Prohibited (from [RFC 1122](https://tools.ietf.org/html/rfc1122))
+    HostProhibited,
+    /// Destination Network Unreachable for Type of Service (from [RFC 1122](https://tools.ietf.org/html/rfc1122))
+    TosNetwork,
+    /// Destination Host Unreachable for Type of Service (from [RFC 1122](https://tools.ietf.org/html/rfc1122))
+    TosHost,
+    /// Cannot forward because packet administratively filtered (from [RFC 1812](https://tools.ietf.org/html/rfc1812))
+    FilterProhibited,
+    /// Required level of precidence not supported (from [RFC 1812](https://tools.ietf.org/html/rfc1812))
+    HostPrecedenceViolation,
+    /// Packet was below minimum precidence (from [RFC 1812](https://tools.ietf.org/html/rfc1812))
+    PrecedenceCutoff,
+}
+
+impl DestUnreachableHeader {
+    /// Tries to convert the code [`u8`] value and next_hop_mtu to a [`DestUnreachableHeader`] value.
+    ///
+    /// Returns [`None`] in case the code value is not known as a destination unreachable code.
+    pub fn from_values(code_u8: u8, next_hop_mtu: u16) -> Option<DestUnreachableHeader> {
+        use crate::icmpv4::{DestUnreachableHeader::*, *};
+        match code_u8 {
+            CODE_DST_UNREACH_NET => Some(Network),
+            CODE_DST_UNREACH_HOST => Some(Host),
+            CODE_DST_UNREACH_PROTOCOL => Some(Protocol),
+            CODE_DST_UNREACH_PORT => Some(Port),
+            CODE_DST_UNREACH_NEED_FRAG => Some(FragmentationNeeded { next_hop_mtu }),
+            CODE_DST_UNREACH_SOURCE_ROUTE_FAILED => Some(SourceRouteFailed),
+            CODE_DST_UNREACH_NET_UNKNOWN => Some(NetworkUnknown),
+            CODE_DST_UNREACH_HOST_UNKNOWN => Some(HostUnknown),
+            CODE_DST_UNREACH_ISOLATED => Some(Isolated),
+            CODE_DST_UNREACH_NET_PROHIB => Some(NetworkProhibited),
+            CODE_DST_UNREACH_HOST_PROHIB => Some(HostProhibited),
+            CODE_DST_UNREACH_TOS_NET => Some(TosNetwork),
+            CODE_DST_UNREACH_TOS_HOST => Some(TosHost),
+            CODE_DST_UNREACH_FILTER_PROHIB => Some(FilterProhibited),
+            CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION => Some(HostPrecedenceViolation),
+            CODE_DST_UNREACH_PRECEDENCE_CUTOFF => Some(PrecedenceCutoff),
+            _ => None,
+        }
+    }
+
+    /// Returns the icmp code value of the destination unreachable packet.
+    #[inline]
+    pub fn code_u8(&self) -> u8 {
+        use crate::icmpv4::{DestUnreachableHeader::*, *};
+        match self {
+            Network => CODE_DST_UNREACH_NET,
+            Host => CODE_DST_UNREACH_HOST,
+            Protocol => CODE_DST_UNREACH_PROTOCOL,
+            Port => CODE_DST_UNREACH_PORT,
+            FragmentationNeeded { next_hop_mtu: _ } => CODE_DST_UNREACH_NEED_FRAG,
+            SourceRouteFailed => CODE_DST_UNREACH_SOURCE_ROUTE_FAILED,
+            NetworkUnknown => CODE_DST_UNREACH_NET_UNKNOWN,
+            HostUnknown => CODE_DST_UNREACH_HOST_UNKNOWN,
+            Isolated => CODE_DST_UNREACH_ISOLATED,
+            NetworkProhibited => CODE_DST_UNREACH_NET_PROHIB,
+            HostProhibited => CODE_DST_UNREACH_HOST_PROHIB,
+            TosNetwork => CODE_DST_UNREACH_TOS_NET,
+            TosHost => CODE_DST_UNREACH_TOS_HOST,
+            FilterProhibited => CODE_DST_UNREACH_FILTER_PROHIB,
+            HostPrecedenceViolation => CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION,
+            PrecedenceCutoff => CODE_DST_UNREACH_PRECEDENCE_CUTOFF,
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::icmpv4::*;
+    use alloc::format;
+    use proptest::prelude::*;
+
+    fn conversion_values(next_hop_mtu: u16) -> [(u8, DestUnreachableHeader); 16] {
+        use DestUnreachableHeader::*;
+        [
+            (CODE_DST_UNREACH_NET, Network),
+            (CODE_DST_UNREACH_HOST, Host),
+            (CODE_DST_UNREACH_PROTOCOL, Protocol),
+            (CODE_DST_UNREACH_PORT, Port),
+            (
+                CODE_DST_UNREACH_NEED_FRAG,
+                FragmentationNeeded { next_hop_mtu },
+            ),
+            (CODE_DST_UNREACH_SOURCE_ROUTE_FAILED, SourceRouteFailed),
+            (CODE_DST_UNREACH_NET_UNKNOWN, NetworkUnknown),
+            (CODE_DST_UNREACH_HOST_UNKNOWN, HostUnknown),
+            (CODE_DST_UNREACH_ISOLATED, Isolated),
+            (CODE_DST_UNREACH_NET_PROHIB, NetworkProhibited),
+            (CODE_DST_UNREACH_HOST_PROHIB, HostProhibited),
+            (CODE_DST_UNREACH_TOS_NET, TosNetwork),
+            (CODE_DST_UNREACH_TOS_HOST, TosHost),
+            (CODE_DST_UNREACH_FILTER_PROHIB, FilterProhibited),
+            (
+                CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION,
+                HostPrecedenceViolation,
+            ),
+            (CODE_DST_UNREACH_PRECEDENCE_CUTOFF, PrecedenceCutoff),
+        ]
+    }
+
+    proptest! {
+        #[test]
+        fn from_values(
+            next_hop_mtu in any::<u16>(),
+        ) {
+            // valid values
+            {
+                let valid_values = conversion_values(next_hop_mtu);
+                for t in valid_values {
+                    assert_eq!(Some(t.1), DestUnreachableHeader::from_values(t.0, next_hop_mtu));
+                }
+            }
+            // invalid values
+            for code_u8 in 16u8..=u8::MAX {
+                assert_eq!(None, DestUnreachableHeader::from_values(code_u8, next_hop_mtu));
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn code_u8(
+            next_hop_mtu in any::<u16>(),
+        ) {
+            let valid_values = conversion_values(next_hop_mtu);
+            for t in valid_values {
+                assert_eq!(t.0, t.1.code_u8());
+            }
+        }
+    }
+
+    #[test]
+    fn clone_eq() {
+        use DestUnreachableHeader::*;
+        let values = [
+            Network,
+            Host,
+            Protocol,
+            Port,
+            FragmentationNeeded { next_hop_mtu: 0 },
+            SourceRouteFailed,
+            NetworkUnknown,
+            HostUnknown,
+            Isolated,
+            NetworkProhibited,
+            HostProhibited,
+            TosNetwork,
+            TosHost,
+            FilterProhibited,
+            HostPrecedenceViolation,
+            PrecedenceCutoff,
+        ];
+        for value in values {
+            assert_eq!(value.clone(), value);
+        }
+    }
+
+    #[test]
+    fn debug() {
+        use DestUnreachableHeader::*;
+        let tests = [
+            ("Network", Network),
+            ("Host", Host),
+            ("Protocol", Protocol),
+            ("Port", Port),
+            (
+                "FragmentationNeeded { next_hop_mtu: 0 }",
+                FragmentationNeeded { next_hop_mtu: 0 },
+            ),
+            ("SourceRouteFailed", SourceRouteFailed),
+            ("NetworkUnknown", NetworkUnknown),
+            ("HostUnknown", HostUnknown),
+            ("Isolated", Isolated),
+            ("NetworkProhibited", NetworkProhibited),
+            ("HostProhibited", HostProhibited),
+            ("TosNetwork", TosNetwork),
+            ("TosHost", TosHost),
+            ("FilterProhibited", FilterProhibited),
+            ("HostPrecedenceViolation", HostPrecedenceViolation),
+            ("PrecedenceCutoff", PrecedenceCutoff),
+        ];
+        for t in tests {
+            assert_eq!(t.0, format!("{:?}", t.1));
+        }
+    }
+}
diff --git a/src/transport/icmpv4/mod.rs b/src/transport/icmpv4/mod.rs
new file mode 100644
index 0000000..e785a8e
--- /dev/null
+++ b/src/transport/icmpv4/mod.rs
@@ -0,0 +1,204 @@
+mod dest_unreachable_header;
+pub use dest_unreachable_header::*;
+
+mod parameter_problem_header;
+pub use parameter_problem_header::*;
+
+mod redirect_code;
+pub use redirect_code::*;
+
+mod redirect_header;
+pub use redirect_header::*;
+
+mod time_exceeded_code;
+pub use time_exceeded_code::*;
+
+mod timestamp_message;
+pub use timestamp_message::*;
+
+/// ICMPv4 type value indicating a "Echo Reply" message (defined in [RFC 792](https://tools.ietf.org/html/rfc792)).
+pub const TYPE_ECHO_REPLY: u8 = 0;
+
+/// ICMPv4 type value indicating a "Destination Unreachable" message (defined in [RFC 792](https://tools.ietf.org/html/rfc792)).
+pub const TYPE_DEST_UNREACH: u8 = 3;
+
+/// ICMPv4 type value indicating a "Source Quench (Deprecated)" message (defined in in [RFC 792](https://tools.ietf.org/html/rfc792), deprecated in [RFC 6633](https://tools.ietf.org/html/rfc6633)).
+pub const TYPE_SOURCE_QUENCH: u8 = 4;
+
+/// ICMPv4 type value indicating a "Redirect" message (defined in [RFC 792](https://tools.ietf.org/html/rfc792)).
+pub const TYPE_REDIRECT: u8 = 5;
+
+/// ICMPv4 type value indicating a "Alternate Host Address (Deprecated)" message (deprecated in [RFC 6918](https://tools.ietf.org/html/rfc6918)).
+pub const TYPE_ALTERNATE_HOST_ADDRESS: u8 = 6;
+
+/// ICMPv4 type value indicating a "Echo Request" message (defined in [RFC 792](https://tools.ietf.org/html/rfc792)).
+pub const TYPE_ECHO_REQUEST: u8 = 8;
+
+/// ICMPv4 type value indicating a "Router Advertisement" message (defined in [RFC 1256](https://tools.ietf.org/html/rfc1256)).
+pub const TYPE_ROUTER_ADVERTISEMENT: u8 = 9;
+
+/// ICMPv4 type value indicating a "Router Solicitation" message (defined in [RFC 1256](https://tools.ietf.org/html/rfc1256)).
+pub const TYPE_ROUTER_SOLICITATION: u8 = 10;
+
+/// ICMPv4 type value indicating a "Time Exceeded" message (defined in [RFC 792](https://tools.ietf.org/html/rfc792)).
+pub const TYPE_TIME_EXCEEDED: u8 = 11;
+
+/// ICMPv4 type value indicating a "Parameter Problem" message (defined in [RFC 792](https://tools.ietf.org/html/rfc792)).
+pub const TYPE_PARAMETER_PROBLEM: u8 = 12;
+
+/// ICMPv4 type value indicating a "Timestamp" message (defined in [RFC 792](https://tools.ietf.org/html/rfc792)).
+pub const TYPE_TIMESTAMP: u8 = 13;
+
+/// ICMPv4 type value indicating a "Timestamp Reply" message (defined in [RFC 792](https://tools.ietf.org/html/rfc792)).
+pub const TYPE_TIMESTAMP_REPLY: u8 = 14;
+
+/// ICMPv4 type value indicating a "Information Request (Deprecated)" message (defined in in [RFC 792](https://tools.ietf.org/html/rfc792), deprecated in [RFC 6918](https://tools.ietf.org/html/rfc6918)).
+pub const TYPE_INFO_REQUEST: u8 = 15;
+
+/// ICMPv4 type value indicating a "Information Reply (Deprecated)" message (defined in in [RFC 792](https://tools.ietf.org/html/rfc792), deprecated in [RFC 6918](https://tools.ietf.org/html/rfc6918)).
+pub const TYPE_INFO_REPLY: u8 = 16;
+
+/// ICMPv4 type value indicating a "Address Mask Request (Deprecated)" message (defined in in [RFC 950](https://tools.ietf.org/html/rfc950), deprecated in [RFC 6918](https://tools.ietf.org/html/rfc6918)).
+pub const TYPE_ADDRESS: u8 = 17;
+
+/// ICMPv4 type value indicating a "Address Mask Reply (Deprecated)" message (defined in in [RFC 950](https://tools.ietf.org/html/rfc950), deprecated in [RFC 6918](https://tools.ietf.org/html/rfc6918)).
+pub const TYPE_ADDRESSREPLY: u8 = 18;
+
+/// ICMP destination unreachable code for "Net Unreachable" (defined in [RFC 792](https://tools.ietf.org/html/rfc792))
+pub const CODE_DST_UNREACH_NET: u8 = 0;
+
+/// ICMP destination unreachable code for "Host Unreachable" (defined in [RFC 792](https://tools.ietf.org/html/rfc792))
+pub const CODE_DST_UNREACH_HOST: u8 = 1;
+
+/// ICMP destination unreachable code for "Protocol Unreachable" (defined in [RFC 792](https://tools.ietf.org/html/rfc792))
+pub const CODE_DST_UNREACH_PROTOCOL: u8 = 2;
+
+/// ICMP destination unreachable code for "Port Unreachable" (defined in [RFC 792](https://tools.ietf.org/html/rfc792))
+pub const CODE_DST_UNREACH_PORT: u8 = 3;
+
+/// ICMP destination unreachable code for "Fragmentation Needed and Don't Fragment was Set" (defined in [RFC 792](https://tools.ietf.org/html/rfc792))
+pub const CODE_DST_UNREACH_NEED_FRAG: u8 = 4;
+
+/// ICMP destination unreachable code for "Source Route Failed" (defined in [RFC 792](https://tools.ietf.org/html/rfc792))
+pub const CODE_DST_UNREACH_SOURCE_ROUTE_FAILED: u8 = 5;
+
+/// ICMP destination unreachable code for "Destination Network Unknown" (defined in [RFC 1122](https://tools.ietf.org/html/rfc1122))
+pub const CODE_DST_UNREACH_NET_UNKNOWN: u8 = 6;
+
+/// ICMP destination unreachable code for "Destination Host Unknown" (defined in [RFC 1122](https://tools.ietf.org/html/rfc1122))
+pub const CODE_DST_UNREACH_HOST_UNKNOWN: u8 = 7;
+
+/// ICMP destination unreachable code for "Source Host Isolated" (defined in [RFC 1122](https://tools.ietf.org/html/rfc1122))
+pub const CODE_DST_UNREACH_ISOLATED: u8 = 8;
+
+/// ICMP destination unreachable code for "Communication with Destination Network is Administratively Prohibited" (defined in [RFC 1122](https://tools.ietf.org/html/rfc1122))
+pub const CODE_DST_UNREACH_NET_PROHIB: u8 = 9;
+
+/// ICMP destination unreachable code for "Communication with Destination Host is Administratively Prohibited" (defined in [RFC 1122](https://tools.ietf.org/html/rfc1122))
+pub const CODE_DST_UNREACH_HOST_PROHIB: u8 = 10;
+
+/// ICMP destination unreachable code for "Destination Network Unreachable for Type of Service" (defined in [RFC 1122](https://tools.ietf.org/html/rfc1122))
+pub const CODE_DST_UNREACH_TOS_NET: u8 = 11;
+
+/// ICMP destination unreachable code for "Destination Host Unreachable for Type of Service" (defined in [RFC 1122](https://tools.ietf.org/html/rfc1122))
+pub const CODE_DST_UNREACH_TOS_HOST: u8 = 12;
+
+/// ICMP destination unreachable code for "Communication Administratively Prohibited" (defined in [RFC 1812](https://tools.ietf.org/html/rfc1812))
+pub const CODE_DST_UNREACH_FILTER_PROHIB: u8 = 13;
+
+/// ICMP destination unreachable code for "Host Precedence Violation" (defined in [RFC 1812](https://tools.ietf.org/html/rfc1812))
+pub const CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION: u8 = 14;
+
+/// ICMP destination unreachable code for "Precedence cutoff in effect" (defined in [RFC 1812](https://tools.ietf.org/html/rfc1812))
+pub const CODE_DST_UNREACH_PRECEDENCE_CUTOFF: u8 = 15;
+
+/// ICMPv4 "Redirect" code value for "Redirect Datagram for the Network (or subnet)".
+pub const CODE_REDIRECT_FOR_NETWORK: u8 = 0;
+
+/// ICMPv4 "Redirect" code value for "Redirect Datagram for the Host".
+pub const CODE_REDIRECT_FOR_HOST: u8 = 1;
+
+/// ICMPv4 "Redirect" code value for "Redirect Datagram for the Type of Service and Network".
+pub const CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK: u8 = 2;
+
+/// ICMPv4 "Redirect" code value for "Redirect Datagram for the Type of Service and Host".
+pub const CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST: u8 = 3;
+
+/// ICMPv4 "Time Exceeded" code value for "Time to Live exceeded in Transit".
+pub const CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT: u8 = 0;
+
+/// ICMPv4 "Time Exceeded" code value for "Fragment Reassembly Time Exceeded".
+pub const CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED: u8 = 1;
+
+/// ICMPv4 "Parameter Problem" code value for "Pointer indicates the error".
+pub const CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR: u8 = 0;
+
+/// ICMPv4 "Parameter Problem" code value for "Missing a Required Option".
+pub const CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION: u8 = 1;
+
+/// ICMPv4 "Parameter Problem" code value for "Bad Length".
+pub const CODE_PARAMETER_PROBLEM_BAD_LENGTH: u8 = 2;
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn constants() {
+        // icmp type numbers according to
+        // https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-types
+        assert_eq!(TYPE_ECHO_REPLY, 0);
+        assert_eq!(TYPE_DEST_UNREACH, 3);
+        assert_eq!(TYPE_SOURCE_QUENCH, 4);
+        assert_eq!(TYPE_REDIRECT, 5);
+        assert_eq!(TYPE_ALTERNATE_HOST_ADDRESS, 6);
+        assert_eq!(TYPE_ECHO_REQUEST, 8);
+        assert_eq!(TYPE_ROUTER_ADVERTISEMENT, 9);
+        assert_eq!(TYPE_ROUTER_SOLICITATION, 10);
+        assert_eq!(TYPE_TIME_EXCEEDED, 11);
+        assert_eq!(TYPE_PARAMETER_PROBLEM, 12);
+        assert_eq!(TYPE_TIMESTAMP, 13);
+        assert_eq!(TYPE_TIMESTAMP_REPLY, 14);
+        assert_eq!(TYPE_INFO_REQUEST, 15);
+        assert_eq!(TYPE_INFO_REPLY, 16);
+        assert_eq!(TYPE_ADDRESS, 17);
+        assert_eq!(TYPE_ADDRESSREPLY, 18);
+
+        // destination unreachable code numbers according to
+        // https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-codes-3
+        assert_eq!(0, CODE_DST_UNREACH_NET);
+        assert_eq!(1, CODE_DST_UNREACH_HOST);
+        assert_eq!(2, CODE_DST_UNREACH_PROTOCOL);
+        assert_eq!(3, CODE_DST_UNREACH_PORT);
+        assert_eq!(4, CODE_DST_UNREACH_NEED_FRAG);
+        assert_eq!(5, CODE_DST_UNREACH_SOURCE_ROUTE_FAILED);
+        assert_eq!(6, CODE_DST_UNREACH_NET_UNKNOWN);
+        assert_eq!(7, CODE_DST_UNREACH_HOST_UNKNOWN);
+        assert_eq!(8, CODE_DST_UNREACH_ISOLATED);
+        assert_eq!(9, CODE_DST_UNREACH_NET_PROHIB);
+        assert_eq!(10, CODE_DST_UNREACH_HOST_PROHIB);
+        assert_eq!(11, CODE_DST_UNREACH_TOS_NET);
+        assert_eq!(12, CODE_DST_UNREACH_TOS_HOST);
+        assert_eq!(13, CODE_DST_UNREACH_FILTER_PROHIB);
+        assert_eq!(14, CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION);
+        assert_eq!(15, CODE_DST_UNREACH_PRECEDENCE_CUTOFF);
+
+        // redirect code numbers according to
+        // https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-codes-5
+        assert_eq!(0, CODE_REDIRECT_FOR_NETWORK);
+        assert_eq!(1, CODE_REDIRECT_FOR_HOST);
+        assert_eq!(2, CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK);
+        assert_eq!(3, CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST);
+
+        // time exceeded code numbers according to
+        // https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-codes-11
+        assert_eq!(0, CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT);
+        assert_eq!(1, CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED);
+
+        // parameter problem code numbers according to
+        // https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-codes-12
+        assert_eq!(0, CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR);
+        assert_eq!(1, CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION);
+        assert_eq!(2, CODE_PARAMETER_PROBLEM_BAD_LENGTH);
+    }
+}
diff --git a/src/transport/icmpv4/parameter_problem_header.rs b/src/transport/icmpv4/parameter_problem_header.rs
new file mode 100644
index 0000000..000efbd
--- /dev/null
+++ b/src/transport/icmpv4/parameter_problem_header.rs
@@ -0,0 +1,75 @@
+/// The header of an ICMPv4 Parameter Problems (contents up to
+/// the offending ip header).
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum ParameterProblemHeader {
+    /// Identifies the octet where an error was detected.
+    ///
+    /// The value is the pointer pointing to the offending octet in
+    /// the offending packet.
+    PointerIndicatesError(u8),
+    /// Missing a Required Option
+    MissingRequiredOption,
+    /// Bad Length
+    BadLength,
+}
+
+impl ParameterProblemHeader {
+    /// Tries to convert the code [`u8`] value and pointer to a [`ParameterProblemHeader`] value.
+    ///
+    /// Returns [`None`] in case the code value is not known as a parameter problem code.
+    pub fn from_values(code_u8: u8, pointer: u8) -> Option<ParameterProblemHeader> {
+        use super::{ParameterProblemHeader::*, *};
+        match code_u8 {
+            CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR => Some(PointerIndicatesError(pointer)),
+            CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION => Some(MissingRequiredOption),
+            CODE_PARAMETER_PROBLEM_BAD_LENGTH => Some(BadLength),
+            _ => None,
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::icmpv4::{ParameterProblemHeader::*, *};
+    use alloc::format;
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn from_values(pointer in any::<u8>()) {
+            {
+                let tests = [
+                    (CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR, PointerIndicatesError(pointer)),
+                    (CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION, MissingRequiredOption),
+                    (CODE_PARAMETER_PROBLEM_BAD_LENGTH, BadLength),
+                ];
+                for t in tests {
+                    assert_eq!(Some(t.1), ParameterProblemHeader::from_values(t.0, pointer));
+                }
+            }
+            for code_u8 in 3..=u8::MAX {
+                assert_eq!(None, ParameterProblemHeader::from_values(code_u8, pointer));
+            }
+        }
+    }
+
+    #[test]
+    fn clone_eq() {
+        let tests = [PointerIndicatesError(0), MissingRequiredOption, BadLength];
+        for t in tests {
+            assert_eq!(t.clone(), t);
+        }
+    }
+
+    #[test]
+    fn debug() {
+        let tests = [
+            ("PointerIndicatesError(0)", PointerIndicatesError(0)),
+            ("MissingRequiredOption", MissingRequiredOption),
+            ("BadLength", BadLength),
+        ];
+        for t in tests {
+            assert_eq!(t.0, format!("{:?}", t.1));
+        }
+    }
+}
diff --git a/src/transport/icmpv4/redirect_code.rs b/src/transport/icmpv4/redirect_code.rs
new file mode 100644
index 0000000..487602b
--- /dev/null
+++ b/src/transport/icmpv4/redirect_code.rs
@@ -0,0 +1,115 @@
+/// Code value in an ICMPv4 Redirect message.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum RedirectCode {
+    /// Redirect Datagram for the Network (or subnet)
+    RedirectForNetwork = 0,
+    /// Redirect Datagram for the Host
+    RedirectForHost = 1,
+    /// Redirect Datagram for the Type of Service and Network
+    RedirectForTypeOfServiceAndNetwork = 2,
+    /// Redirect datagrams for the Type of Service and Host
+    RedirectForTypeOfServiceAndHost = 3,
+}
+
+impl RedirectCode {
+    /// Tries to convert a code [`u8`] value to a [`RedirectCode`] value.
+    ///
+    /// Returns [`None`] in case the code value is not known as a redirect code.
+    #[inline]
+    pub fn from_u8(code_u8: u8) -> Option<RedirectCode> {
+        use crate::icmpv4::{RedirectCode::*, *};
+        match code_u8 {
+            CODE_REDIRECT_FOR_NETWORK => Some(RedirectForNetwork),
+            CODE_REDIRECT_FOR_HOST => Some(RedirectForHost),
+            CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK => Some(RedirectForTypeOfServiceAndNetwork),
+            CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST => Some(RedirectForTypeOfServiceAndHost),
+            _ => None,
+        }
+    }
+
+    /// Returns the [`u8`] value of the code.
+    #[inline]
+    pub fn code_u8(&self) -> u8 {
+        *self as u8
+    }
+}
+
+#[cfg(test)]
+
+mod test {
+    use crate::icmpv4::{RedirectCode::*, *};
+    use alloc::format;
+
+    #[test]
+    fn from_u8() {
+        let tests = [
+            (CODE_REDIRECT_FOR_NETWORK, RedirectForNetwork),
+            (CODE_REDIRECT_FOR_HOST, RedirectForHost),
+            (
+                CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK,
+                RedirectForTypeOfServiceAndNetwork,
+            ),
+            (
+                CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST,
+                RedirectForTypeOfServiceAndHost,
+            ),
+        ];
+        for t in tests {
+            assert_eq!(Some(t.1), RedirectCode::from_u8(t.0));
+        }
+        for code_u8 in 4..=u8::MAX {
+            assert_eq!(None, RedirectCode::from_u8(code_u8));
+        }
+    }
+
+    #[test]
+    fn code_u8() {
+        let tests = [
+            (CODE_REDIRECT_FOR_NETWORK, RedirectForNetwork),
+            (CODE_REDIRECT_FOR_HOST, RedirectForHost),
+            (
+                CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK,
+                RedirectForTypeOfServiceAndNetwork,
+            ),
+            (
+                CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST,
+                RedirectForTypeOfServiceAndHost,
+            ),
+        ];
+        for t in tests {
+            assert_eq!(t.1.code_u8(), t.0);
+        }
+    }
+
+    #[test]
+    fn clone_eq() {
+        let tests = [
+            RedirectForNetwork,
+            RedirectForHost,
+            RedirectForTypeOfServiceAndNetwork,
+            RedirectForTypeOfServiceAndHost,
+        ];
+        for t in tests {
+            assert_eq!(t.clone(), t);
+        }
+    }
+
+    #[test]
+    fn debug() {
+        let tests = [
+            ("RedirectForNetwork", RedirectForNetwork),
+            ("RedirectForHost", RedirectForHost),
+            (
+                "RedirectForTypeOfServiceAndNetwork",
+                RedirectForTypeOfServiceAndNetwork,
+            ),
+            (
+                "RedirectForTypeOfServiceAndHost",
+                RedirectForTypeOfServiceAndHost,
+            ),
+        ];
+        for t in tests {
+            assert_eq!(t.0, format!("{:?}", t.1));
+        }
+    }
+}
diff --git a/src/transport/icmpv4/redirect_header.rs b/src/transport/icmpv4/redirect_header.rs
new file mode 100644
index 0000000..86d9dc5
--- /dev/null
+++ b/src/transport/icmpv4/redirect_header.rs
@@ -0,0 +1,37 @@
+use super::*;
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct RedirectHeader {
+    pub code: RedirectCode,
+    pub gateway_internet_address: [u8; 4],
+}
+
+#[cfg(test)]
+mod test {
+    use crate::icmpv4::{RedirectCode::*, *};
+    use alloc::format;
+
+    #[test]
+    fn clone_eq() {
+        let v = RedirectHeader {
+            code: RedirectForNetwork,
+            gateway_internet_address: [0; 4],
+        };
+        assert_eq!(v.clone(), v);
+    }
+
+    #[test]
+    fn debug() {
+        let v = RedirectHeader {
+            code: RedirectForNetwork,
+            gateway_internet_address: [0; 4],
+        };
+        assert_eq!(
+            format!("{:?}", v),
+            format!(
+                "RedirectHeader {{ code: {:?}, gateway_internet_address: {:?} }}",
+                v.code, v.gateway_internet_address
+            )
+        );
+    }
+}
diff --git a/src/transport/icmpv4/time_exceeded_code.rs b/src/transport/icmpv4/time_exceeded_code.rs
new file mode 100644
index 0000000..f74fe5f
--- /dev/null
+++ b/src/transport/icmpv4/time_exceeded_code.rs
@@ -0,0 +1,89 @@
+use super::*;
+
+/// Code values for ICMPv4 time exceeded message.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum TimeExceededCode {
+    /// Time-to-live exceeded in transit.
+    TtlExceededInTransit = 0,
+    /// Fragment reassembly time exceeded.
+    FragmentReassemblyTimeExceeded = 1,
+}
+
+impl TimeExceededCode {
+    /// Tries to convert a code [`u8`] value to a [`TimeExceededCode`] value.
+    ///
+    /// Returns [`None`] in case the code value is not known as a time exceeded code.
+    #[inline]
+    pub fn from_u8(code_u8: u8) -> Option<TimeExceededCode> {
+        use TimeExceededCode::*;
+        match code_u8 {
+            CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT => Some(TtlExceededInTransit),
+            CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED => {
+                Some(FragmentReassemblyTimeExceeded)
+            }
+            _ => None,
+        }
+    }
+
+    /// Returns the [`u8`] value of the code.
+    #[inline]
+    pub fn code_u8(&self) -> u8 {
+        *self as u8
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::icmpv4::{TimeExceededCode::*, *};
+    use alloc::format;
+
+    #[test]
+    fn from_u8() {
+        assert_eq!(
+            TimeExceededCode::from_u8(CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT),
+            Some(TtlExceededInTransit)
+        );
+        assert_eq!(
+            TimeExceededCode::from_u8(CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED),
+            Some(FragmentReassemblyTimeExceeded)
+        );
+
+        for code_u8 in 2..=u8::MAX {
+            assert_eq!(None, TimeExceededCode::from_u8(code_u8));
+        }
+    }
+
+    #[test]
+    fn code_u8() {
+        assert_eq!(
+            TtlExceededInTransit.code_u8(),
+            CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT
+        );
+        assert_eq!(
+            FragmentReassemblyTimeExceeded.code_u8(),
+            CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED
+        );
+    }
+
+    #[test]
+    fn debug() {
+        let values = [
+            ("TtlExceededInTransit", TtlExceededInTransit),
+            (
+                "FragmentReassemblyTimeExceeded",
+                FragmentReassemblyTimeExceeded,
+            ),
+        ];
+        for (expected, input) in values {
+            assert_eq!(expected, format!("{:?}", input));
+        }
+    }
+
+    #[test]
+    fn clone_eq() {
+        let values = [TtlExceededInTransit, FragmentReassemblyTimeExceeded];
+        for value in values {
+            assert_eq!(value.clone(), value);
+        }
+    }
+}
diff --git a/src/transport/icmpv4/timestamp_message.rs b/src/transport/icmpv4/timestamp_message.rs
new file mode 100644
index 0000000..047312d
--- /dev/null
+++ b/src/transport/icmpv4/timestamp_message.rs
@@ -0,0 +1,91 @@
+/// A ICMPv4 timestamp or timestamp response message.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct TimestampMessage {
+    pub id: u16,
+    pub seq: u16,
+    pub originate_timestamp: u32,
+    pub receive_timestamp: u32,
+    pub transmit_timestamp: u32,
+}
+
+impl TimestampMessage {
+    /// Deprecated use [`TimestampMessage::LEN`] instead.
+    #[deprecated(since = "0.14.0", note = "Use `TimestampMessage::LEN` instead")]
+    pub const SERIALIZED_SIZE: usize = 20;
+
+    /// The size in bytes/octets of a timestamp request or timestamp response message.
+    pub const LEN: usize = 20;
+
+    /// Decodes the timestamp message part of an ICMPv4 message.
+    pub fn from_bytes(bytes: [u8; 16]) -> TimestampMessage {
+        TimestampMessage {
+            id: u16::from_be_bytes([bytes[0], bytes[1]]),
+            seq: u16::from_be_bytes([bytes[2], bytes[3]]),
+            originate_timestamp: u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
+            receive_timestamp: u32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]),
+            transmit_timestamp: u32::from_be_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::icmpv4::*;
+    use alloc::format;
+    use proptest::prelude::*;
+
+    #[test]
+    fn constants() {
+        assert_eq!(20, TimestampMessage::LEN);
+    }
+
+    proptest! {
+        #[test]
+        fn from_bytes(bytes in any::<[u8;16]>()) {
+            assert_eq!(
+                TimestampMessage::from_bytes(bytes),
+                TimestampMessage{
+                    id: u16::from_be_bytes([bytes[0], bytes[1]]),
+                    seq: u16::from_be_bytes([bytes[2], bytes[3]]),
+                    originate_timestamp: u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
+                    receive_timestamp: u32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]),
+                    transmit_timestamp: u32::from_be_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]),
+                }
+            );
+        }
+    }
+
+    #[test]
+    fn clone_eq() {
+        let v = TimestampMessage {
+            id: 0,
+            seq: 0,
+            originate_timestamp: 0,
+            receive_timestamp: 0,
+            transmit_timestamp: 0,
+        };
+        assert_eq!(v.clone(), v);
+    }
+
+    #[test]
+    fn debug() {
+        let v = TimestampMessage {
+            id: 0,
+            seq: 0,
+            originate_timestamp: 0,
+            receive_timestamp: 0,
+            transmit_timestamp: 0,
+        };
+        assert_eq!(
+            format!("{:?}", v),
+            format!(
+                "TimestampMessage {{ id: {:?}, seq: {:?}, originate_timestamp: {:?}, receive_timestamp: {:?}, transmit_timestamp: {:?} }}",
+                v.id,
+                v.seq,
+                v.originate_timestamp,
+                v.receive_timestamp,
+                v.transmit_timestamp,
+            )
+        );
+    }
+}
diff --git a/src/transport/icmpv4_header.rs b/src/transport/icmpv4_header.rs
new file mode 100644
index 0000000..6b24f1c
--- /dev/null
+++ b/src/transport/icmpv4_header.rs
@@ -0,0 +1,759 @@
+use crate::*;
+use arrayvec::ArrayVec;
+
+/// A header of an ICMPv4 packet.
+///
+/// What is part of the header depends on the ICMPv4 type
+/// and code. But usually the static sized elements are part
+/// of the header.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct Icmpv4Header {
+    /// Type & type specific values & code.
+    pub icmp_type: Icmpv4Type,
+    /// Checksum in the ICMP header.
+    pub checksum: u16,
+}
+
+impl Icmpv4Header {
+    /// Minimum number of bytes/octets an Icmpv4Header takes up
+    /// in serialized form.
+    pub const MIN_LEN: usize = 8;
+
+    /// Deprecated, use [`Icmpv4Header::MIN_LEN`] instead.
+    #[deprecated(since = "0.14.0", note = "Please use Icmpv4Header::MIN_LEN instead")]
+    pub const MIN_SERIALIZED_SIZE: usize = 8;
+
+    /// Maximum number of bytes/octets an Icmpv4Header takes up
+    /// in serialized form.
+    ///
+    /// Currently this number is determined by the biggest
+    /// supported ICMPv4 header type, which is currently the
+    /// "Timestamp" and "Timestamp Reply Message".
+    pub const MAX_LEN: usize = 20;
+
+    /// Deprecated, use [`Icmpv4Header::MAX_LEN`] instead.
+    #[deprecated(since = "0.14.0", note = "Please use Icmpv4Header::MAX_LEN instead")]
+    pub const MAX_SERIALIZED_SIZE: usize = 20;
+
+    /// Constructs an [`Icmpv4Header`] using the given type
+    /// and the checksum set to 0.
+    pub fn new(icmp_type: Icmpv4Type) -> Icmpv4Header {
+        // Note: will calculate checksum on send
+        Icmpv4Header {
+            icmp_type,
+            checksum: 0,
+        }
+    }
+
+    /// Creates a [`Icmpv4Header`] with a checksum calculated based on the given payload.
+    pub fn with_checksum(icmp_type: Icmpv4Type, payload: &[u8]) -> Icmpv4Header {
+        let checksum = icmp_type.calc_checksum(payload);
+        Icmpv4Header {
+            icmp_type,
+            checksum,
+        }
+    }
+
+    /// Reads an icmp4 header from a slice directly and returns a tuple containing the resulting header & unused part of the slice.
+    #[inline]
+    pub fn from_slice(slice: &[u8]) -> Result<(Icmpv4Header, &[u8]), err::LenError> {
+        let header = Icmpv4Slice::from_slice(slice)?.header();
+        let rest = &slice[header.header_len()..];
+        Ok((header, rest))
+    }
+
+    /// Reads an ICMPv4 header from the given reader.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read<T: std::io::Read + Sized>(reader: &mut T) -> Result<Icmpv4Header, std::io::Error> {
+        let mut bytes = [0u8; Icmpv4Header::MAX_LEN];
+
+        // try reading the initial 8 bytes
+        reader.read_exact(&mut bytes[..8])?;
+
+        match bytes[0] {
+            icmpv4::TYPE_TIMESTAMP_REPLY | icmpv4::TYPE_TIMESTAMP => {
+                if 0 == bytes[1] {
+                    // Timetamp messages need additional data read & it and
+                    // then set the slice correspondently
+                    reader.read_exact(&mut bytes[8..icmpv4::TimestampMessage::LEN])?;
+                    Ok(Icmpv4Slice {
+                        slice: &bytes[..icmpv4::TimestampMessage::LEN],
+                    }
+                    .header())
+                } else {
+                    // fallback to unknown
+                    Ok(Icmpv4Slice { slice: &bytes[..8] }.header())
+                }
+            }
+            _ => Ok(Icmpv4Slice { slice: &bytes[..8] }.header()),
+        }
+    }
+
+    /// Write the ICMPv4 header to the given writer.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
+        writer.write_all(&self.to_bytes())
+    }
+
+    /// Length in bytes/octets of this header type.
+    #[inline]
+    pub fn header_len(&self) -> usize {
+        self.icmp_type.header_len()
+    }
+
+    /// If the ICMP type has a fixed size returns the number of
+    /// bytes that should be present after the header of this type.
+    #[inline]
+    pub fn fixed_payload_size(&self) -> Option<usize> {
+        self.icmp_type.fixed_payload_size()
+    }
+
+    /// Calculates & updates the checksum in the header.
+    ///
+    /// Note this method assumes that all unused bytes/octets
+    /// are filled with zeroes.
+    pub fn update_checksum(&mut self, payload: &[u8]) {
+        self.checksum = self.icmp_type.calc_checksum(payload);
+    }
+
+    /// Converts the header to the on the wire bytes.
+    #[rustfmt::skip]
+    pub fn to_bytes(&self) -> ArrayVec<u8, { Icmpv4Header::MAX_LEN }> {
+        let checksum_be = self.checksum.to_be_bytes();
+        let re_zero =
+            |type_u8: u8, code_u8: u8| -> ArrayVec<u8, { Icmpv4Header::MAX_LEN }> {
+
+                #[rustfmt::skip]
+                let mut re = ArrayVec::from([
+                    type_u8, code_u8, checksum_be[0], checksum_be[1],
+                    0, 0, 0, 0,
+                    0, 0, 0, 0,
+                    0, 0, 0, 0,
+                    0, 0, 0, 0,
+                ]);
+                // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20.
+                unsafe {
+                    re.set_len(8);
+                }
+                re
+            };
+
+        let re_2u16 = |type_u8: u8,
+                       code_u8: u8,
+                       a_u16: u16,
+                       b_u16: u16|
+         -> ArrayVec<u8, { Icmpv4Header::MAX_LEN }> {
+            let a = a_u16.to_be_bytes();
+            let b = b_u16.to_be_bytes();
+
+            #[rustfmt::skip]
+            let mut re = ArrayVec::from([
+                type_u8, code_u8, checksum_be[0], checksum_be[1],
+                a[0], a[1], b[0], b[1],
+                0, 0, 0, 0,
+                0, 0, 0, 0,
+                0, 0, 0, 0,
+            ]);
+            // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20.
+            unsafe {
+                re.set_len(8);
+            }
+            re
+        };
+
+        let re_4u8 = |type_u8: u8,
+                      code_u8: u8,
+                      bytes5to8: [u8; 4]|
+         -> ArrayVec<u8, { Icmpv4Header::MAX_LEN }> {
+
+            #[rustfmt::skip]
+            let mut re = ArrayVec::from([
+                type_u8, code_u8, checksum_be[0], checksum_be[1],
+                bytes5to8[0], bytes5to8[1], bytes5to8[2], bytes5to8[3],
+                0, 0, 0, 0,
+                0, 0, 0, 0,
+                0, 0, 0, 0,
+            ]);
+            // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20.
+            unsafe {
+                re.set_len(8);
+            }
+            re
+        };
+
+        let re_timestamp_msg = |type_u8: u8,
+                                msg: &icmpv4::TimestampMessage|
+         -> ArrayVec<u8, { Icmpv4Header::MAX_LEN }> {
+            let id = msg.id.to_be_bytes();
+            let seq = msg.seq.to_be_bytes();
+            let o = msg.originate_timestamp.to_be_bytes();
+            let r = msg.receive_timestamp.to_be_bytes();
+            let t = msg.transmit_timestamp.to_be_bytes();
+
+            ArrayVec::from([
+                type_u8, 0, checksum_be[0], checksum_be[1],
+                id[0], id[1], seq[0], seq[1],
+                o[0], o[1], o[2], o[3],
+                r[0], r[1], r[2], r[3],
+                t[0], t[1], t[2], t[3],
+            ])
+        };
+
+        use Icmpv4Type::*;
+        use icmpv4::*;
+        match self.icmp_type {
+            Unknown {
+                type_u8,
+                code_u8,
+                bytes5to8,
+            } => re_4u8(type_u8, code_u8, bytes5to8),
+            EchoReply(echo) => re_2u16(TYPE_ECHO_REPLY, 0, echo.id, echo.seq),
+            DestinationUnreachable(ref dest) => {
+                use DestUnreachableHeader::*;
+                match dest {
+                    Network => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET),
+                    Host => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST),
+                    Protocol => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_PROTOCOL),
+                    Port => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_PORT),
+                    FragmentationNeeded { next_hop_mtu } => {
+                        let m_be = next_hop_mtu.to_be_bytes();
+                        re_4u8(
+                            TYPE_DEST_UNREACH,
+                            CODE_DST_UNREACH_NEED_FRAG,
+                            [0, 0, m_be[0], m_be[1]],
+                        )
+                    }
+                    SourceRouteFailed => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_SOURCE_ROUTE_FAILED),
+                    NetworkUnknown => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET_UNKNOWN),
+                    HostUnknown => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST_UNKNOWN),
+                    Isolated => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_ISOLATED),
+                    NetworkProhibited => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET_PROHIB),
+                    HostProhibited => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST_PROHIB),
+                    TosNetwork => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_TOS_NET),
+                    TosHost => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_TOS_HOST),
+                    FilterProhibited => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_FILTER_PROHIB),
+                    HostPrecedenceViolation => re_zero(
+                        TYPE_DEST_UNREACH,
+                        CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION,
+                    ),
+                    PrecedenceCutoff => {
+                        re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_PRECEDENCE_CUTOFF)
+                    }
+                }
+            }
+            Redirect(ref msg) => {
+                re_4u8(TYPE_REDIRECT, msg.code as u8, msg.gateway_internet_address)
+            }
+            EchoRequest(echo) => re_2u16(TYPE_ECHO_REQUEST, 0, echo.id, echo.seq),
+            TimeExceeded(code) => re_zero(TYPE_TIME_EXCEEDED, code as u8),
+            ParameterProblem(ref header) => {
+                use ParameterProblemHeader::*;
+                match header {
+                    PointerIndicatesError(pointer) => re_4u8(
+                        TYPE_PARAMETER_PROBLEM,
+                        CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR,
+                        [*pointer, 0, 0, 0],
+                    ),
+                    MissingRequiredOption => re_zero(
+                        TYPE_PARAMETER_PROBLEM,
+                        CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION,
+                    ),
+                    BadLength => re_zero(TYPE_PARAMETER_PROBLEM, CODE_PARAMETER_PROBLEM_BAD_LENGTH),
+                }
+            }
+            TimestampRequest(ref msg) => re_timestamp_msg(TYPE_TIMESTAMP, msg),
+            TimestampReply(ref msg) => re_timestamp_msg(TYPE_TIMESTAMP_REPLY, msg),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{
+        err::{Layer, LenError},
+        icmpv4::*,
+        test_gens::*,
+        *,
+    };
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    #[test]
+    #[allow(deprecated)]
+    fn constants() {
+        assert_eq!(8, Icmpv4Header::MIN_LEN);
+        assert_eq!(20, Icmpv4Header::MAX_LEN);
+        assert_eq!(8, Icmpv4Header::MIN_SERIALIZED_SIZE);
+        assert_eq!(20, Icmpv4Header::MAX_SERIALIZED_SIZE);
+    }
+
+    proptest! {
+        #[test]
+        fn new(icmpv4_type in icmpv4_type_any()) {
+            assert_eq!(
+                Icmpv4Header {
+                    icmp_type: icmpv4_type.clone(),
+                    checksum: 0,
+                },
+                Icmpv4Header::new(icmpv4_type)
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn with_checksum(
+            icmpv4_type in icmpv4_type_any(),
+            payload in proptest::collection::vec(any::<u8>(), 0..1024),
+        ) {
+            assert_eq!(
+                Icmpv4Header {
+                    icmp_type: icmpv4_type.clone(),
+                    checksum: icmpv4_type.calc_checksum(&payload),
+                },
+                Icmpv4Header::with_checksum(icmpv4_type, &payload)
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            icmpv4_type in icmpv4_type_any(),
+            checksum in any::<u16>(),
+            payload in proptest::collection::vec(any::<u8>(), 0..1024),
+        ) {
+            use Icmpv4Type::*;
+
+            // ok case
+            let header = Icmpv4Header {
+                icmp_type: icmpv4_type.clone(),
+                checksum: checksum,
+            };
+            let buffer = {
+                let mut buffer = Vec::with_capacity(header.header_len() + payload.len());
+                buffer.extend_from_slice(&header.to_bytes());
+
+                match icmpv4_type {
+                    // skip the payoad for the timestamp request (those don't have a payload)
+                    TimestampRequest(_) | TimestampReply(_) => {},
+                    _ => {
+                        buffer.extend_from_slice(&[0u8;36]);
+                    }
+                }
+                buffer
+            };
+            {
+                let (actual, rest) = Icmpv4Header::from_slice(&buffer).unwrap();
+                assert_eq!(actual, header);
+                assert_eq!(rest, &buffer[header.header_len()..]);
+            }
+
+            // error case
+            for bad_len in 0..header.header_len() {
+                assert_eq!(
+                    Icmpv4Header::from_slice(&buffer[..bad_len]),
+                    Err(LenError{
+                        required_len: if bad_len < Icmpv4Header::MIN_LEN {
+                            Icmpv4Header::MIN_LEN
+                        } else {
+                            header.header_len()
+                        },
+                        len: bad_len,
+                        len_source: LenSource::Slice,
+                        layer: if bad_len < Icmpv4Header::MIN_LEN {
+                            Layer::Icmpv4
+                        } else {
+                            use crate::Icmpv4Type::*;
+                            match icmpv4_type {
+                                TimestampRequest(_) => Layer::Icmpv4Timestamp,
+                                TimestampReply(_) => Layer::Icmpv4TimestampReply,
+                                _ => Layer::Icmpv4,
+                            }
+                        },
+                        layer_start_offset: 0,
+                    })
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read(
+            non_timestamp_type in any::<u8>().prop_filter(
+                "type must be a non timestamp type",
+                |v| (*v != icmpv4::TYPE_TIMESTAMP_REPLY && *v != icmpv4::TYPE_TIMESTAMP)
+            ),
+            non_zero_code in 1u8..=u8::MAX,
+            bytes in any::<[u8;icmpv4::TimestampMessage::LEN]>()
+        ) {
+            for (type_u8, code_u8) in [
+                // non timestamp
+                (non_timestamp_type, bytes[1]),
+                // timestamp with zero code
+                (TYPE_TIMESTAMP_REPLY, 0u8),
+                (TYPE_TIMESTAMP, 0u8),
+                // timestamp with non-zero code
+                (TYPE_TIMESTAMP_REPLY, non_zero_code),
+                (TYPE_TIMESTAMP, non_zero_code),
+            ] {
+                let b = {
+                    let mut b = bytes.clone();
+                    b[0] = type_u8;
+                    b[1] = code_u8;
+                    b
+                };
+                let expected = Icmpv4Header::from_slice(&b).unwrap().0;
+
+                // ok case
+                {
+                    let mut cursor = std::io::Cursor::new(&b);
+                    let actual = Icmpv4Header::read(&mut cursor).unwrap();
+                    assert_eq!(expected, actual);
+                    assert_eq!(expected.header_len() as u64, cursor.position());
+                }
+
+                // size error case
+                for bad_len in 0..expected.header_len() {
+                    let mut cursor = std::io::Cursor::new(&(b.as_ref()[..bad_len]));
+                    assert!(Icmpv4Header::read(&mut cursor).is_err());
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write(
+            icmpv4_type in icmpv4_type_any(),
+            checksum in any::<u16>(),
+        ) {
+            let header = Icmpv4Header {
+                icmp_type: icmpv4_type.clone(),
+                checksum,
+            };
+
+            // normal write
+            {
+                let bytes = header.to_bytes();
+                let mut buffer = Vec::with_capacity(header.header_len());
+                header.write(&mut buffer).unwrap();
+                assert_eq!(&bytes[..], &buffer[..]);
+            }
+
+            // error case
+            {
+                for bad_len in 0..icmpv4_type.header_len() {
+                    let mut bytes = [0u8;Icmpv6Header::MAX_LEN];
+                    let mut writer = std::io::Cursor::new(&mut bytes[..bad_len]);
+                    header.write(&mut writer).unwrap_err();
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header_len(
+            checksum in any::<u16>(),
+            icmpv4_type in icmpv4_type_any()
+        ) {
+            let header = Icmpv4Header{
+                icmp_type: icmpv4_type.clone(),
+                checksum,
+            };
+            assert_eq!(header.header_len(), icmpv4_type.header_len());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn fixed_payload_size(
+            checksum in any::<u16>(),
+            icmpv4_type in icmpv4_type_any()
+        ) {
+            let header = Icmpv4Header{
+                icmp_type: icmpv4_type.clone(),
+                checksum,
+            };
+            assert_eq!(header.fixed_payload_size(), icmpv4_type.fixed_payload_size());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn update_checksum(
+            icmpv4_type in icmpv4_type_any(),
+            checksum in any::<u16>(),
+            payload in proptest::collection::vec(any::<u8>(), 0..1024),
+        ) {
+            let mut header = Icmpv4Header {
+                icmp_type: icmpv4_type.clone(),
+                checksum,
+            };
+            header.update_checksum(&payload);
+            assert_eq!(header.checksum, icmpv4_type.calc_checksum(&payload));
+        }
+    }
+
+    proptest! {
+        #[test]
+        #[rustfmt::skip]
+        fn to_bytes(
+            checksum in any::<u16>(),
+            next_hop_mtu in any::<u16>(),
+            redirect_code_u8 in 0u8..=3,
+            gateway_internet_address in any::<[u8;4]>(),
+            time_exceeded_code_u8 in 0u8..=1,
+            id in any::<u16>(),
+            seq in any::<u16>(),
+            originate_timestamp in any::<u32>(),
+            receive_timestamp in any::<u32>(),
+            transmit_timestamp in any::<u32>(),
+            pointer in any::<u8>(),
+            unknown_type_u8 in any::<u8>(),
+            unknown_code_u8 in any::<u8>(),
+            bytes5to8 in any::<[u8;4]>(),
+        ) {
+            use Icmpv4Type::*;
+            use arrayvec::ArrayVec;
+
+            let ts = TimestampMessage{
+                id,
+                seq,
+                originate_timestamp,
+                receive_timestamp,
+                transmit_timestamp,
+            };
+            let ts_bytes = {
+                let id_be = id.to_be_bytes();
+                let seq_be = seq.to_be_bytes();
+                let ot = originate_timestamp.to_be_bytes();
+                let rt = receive_timestamp.to_be_bytes();
+                let tt = transmit_timestamp.to_be_bytes();
+                [
+                    0, 0, 0, 0,
+                    id_be[0], id_be[1], seq_be[0], seq_be[1],
+                    ot[0], ot[1], ot[2], ot[3],
+                    rt[0], rt[1], rt[2], rt[3],
+                    tt[0], tt[1], tt[2], tt[3],
+                ]
+            };
+            let echo = IcmpEchoHeader{
+                id,
+                seq,
+            };
+            let redirect = RedirectHeader{
+                code: RedirectCode::from_u8(redirect_code_u8).unwrap(),
+                gateway_internet_address,
+            };
+
+            // test values with no need for subtests
+            let random_values = [
+                (
+                    Unknown {
+                        type_u8: unknown_type_u8,
+                        code_u8: unknown_code_u8,
+                        bytes5to8: bytes5to8,
+                    },
+                    8,
+                    [
+                        unknown_type_u8, unknown_code_u8, 0, 0,
+                        bytes5to8[0], bytes5to8[1], bytes5to8[2], bytes5to8[3],
+                        0, 0, 0, 0,
+                        0, 0, 0, 0,
+                        0, 0, 0, 0,
+                    ],
+                ),
+                (
+                    EchoReply(echo.clone()),
+                    8,
+                    {
+                        let id_be = id.to_be_bytes();
+                        let seq_be = seq.to_be_bytes();
+                        [
+                            TYPE_ECHO_REPLY, 0, 0, 0,
+                            id_be[0], id_be[1], seq_be[0], seq_be[1],
+                            0, 0, 0, 0,
+                            0, 0, 0, 0,
+                            0, 0, 0, 0,
+                        ]
+                    }
+                ),
+
+                (
+                    Redirect(redirect),
+                    8,
+                    {
+                        let gip = gateway_internet_address;
+                        [
+                            TYPE_REDIRECT, redirect_code_u8, 0, 0,
+                            gip[0], gip[1], gip[2], gip[3],
+                            0, 0, 0, 0,
+                            0, 0, 0, 0,
+                            0, 0, 0, 0,
+                        ]
+                    },
+                ),
+                (
+                    EchoRequest(echo.clone()),
+                    8,
+                    {
+                        let id_be = id.to_be_bytes();
+                        let seq_be = seq.to_be_bytes();
+                        [
+                            TYPE_ECHO_REQUEST, 0, 0, 0,
+                            id_be[0], id_be[1], seq_be[0], seq_be[1],
+                            0, 0, 0, 0,
+                            0, 0, 0, 0,
+                            0, 0, 0, 0,
+                        ]
+                    }
+                ),
+                (
+                    TimeExceeded(TimeExceededCode::from_u8(time_exceeded_code_u8).unwrap()),
+                    8,
+                    [
+                        TYPE_TIME_EXCEEDED, time_exceeded_code_u8, 0, 0,
+                        0, 0, 0, 0,
+                        0, 0, 0, 0,
+                        0, 0, 0, 0,
+                        0, 0, 0, 0,
+                    ],
+                ),
+                (
+                    TimestampRequest(ts.clone()),
+                    20,
+                    {
+                        let mut b = ts_bytes;
+                        b[0] = TYPE_TIMESTAMP;
+                        b
+                    }
+                ),
+                (
+                    TimestampReply(ts),
+                    20,
+                    {
+                        let mut b = ts_bytes;
+                        b[0] = TYPE_TIMESTAMP_REPLY;
+                        b
+                    }
+                ),
+            ];
+
+            for t in random_values {
+                let actual = Icmpv4Header{
+                    icmp_type: t.0.clone(),
+                    checksum,
+                }.to_bytes();
+
+                let mut expected = ArrayVec::from(t.2);
+                unsafe {
+                    expected.set_len(t.1)
+                }
+                let checksum_be = checksum.to_be_bytes();
+                expected[2] = checksum_be[0];
+                expected[3] = checksum_be[1];
+                assert_eq!(expected, actual);
+            }
+
+            // destination unreachable
+            {
+                use DestUnreachableHeader::*;
+                let tests = [
+                    (CODE_DST_UNREACH_NET, [0;2], Network),
+                    (CODE_DST_UNREACH_HOST, [0;2], Host),
+                    (CODE_DST_UNREACH_PROTOCOL, [0;2], Protocol),
+                    (CODE_DST_UNREACH_PORT, [0;2], Port),
+                    (CODE_DST_UNREACH_NEED_FRAG, next_hop_mtu.to_be_bytes(), FragmentationNeeded{ next_hop_mtu }),
+                    (CODE_DST_UNREACH_SOURCE_ROUTE_FAILED, [0;2], SourceRouteFailed),
+                    (CODE_DST_UNREACH_NET_UNKNOWN, [0;2], NetworkUnknown),
+                    (CODE_DST_UNREACH_HOST_UNKNOWN, [0;2], HostUnknown),
+                    (CODE_DST_UNREACH_ISOLATED, [0;2], Isolated),
+                    (CODE_DST_UNREACH_NET_PROHIB, [0;2], NetworkProhibited),
+                    (CODE_DST_UNREACH_HOST_PROHIB, [0;2], HostProhibited),
+                    (CODE_DST_UNREACH_TOS_NET, [0;2], TosNetwork),
+                    (CODE_DST_UNREACH_TOS_HOST, [0;2], TosHost),
+                    (CODE_DST_UNREACH_FILTER_PROHIB, [0;2], FilterProhibited),
+                    (CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION, [0;2], HostPrecedenceViolation),
+                    (CODE_DST_UNREACH_PRECEDENCE_CUTOFF, [0;2], PrecedenceCutoff),
+                ];
+                for t in tests {
+                    let checksum_be = checksum.to_be_bytes();
+                    let mut expected = ArrayVec::from([
+                        TYPE_DEST_UNREACH, t.0, checksum_be[0], checksum_be[1],
+                        0, 0, t.1[0], t.1[1],
+                        0, 0, 0, 0,
+                        0, 0, 0, 0,
+                        0, 0, 0, 0,
+                    ]);
+                    unsafe {
+                        expected.set_len(8);
+                    }
+                    let actual = Icmpv4Header{
+                        icmp_type: DestinationUnreachable(t.2.clone()),
+                        checksum,
+                    }.to_bytes();
+                    assert_eq!(expected, actual);
+                }
+            }
+
+            // parameter problem
+            {
+                use ParameterProblemHeader::*;
+                let tests = [
+                    (CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR, pointer, PointerIndicatesError(pointer)),
+                    (CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION, 0, MissingRequiredOption),
+                    (CODE_PARAMETER_PROBLEM_BAD_LENGTH, 0, BadLength),
+                ];
+                for t in tests {
+                    let checksum_be = checksum.to_be_bytes();
+                    let mut expected = ArrayVec::from([
+                        TYPE_PARAMETER_PROBLEM, t.0, checksum_be[0], checksum_be[1],
+                        t.1, 0, 0, 0,
+                        0, 0, 0, 0,
+                        0, 0, 0, 0,
+                        0, 0, 0, 0,
+                    ]);
+                    unsafe {
+                        expected.set_len(8);
+                    }
+                    let actual = Icmpv4Header{
+                        icmp_type: ParameterProblem(t.2.clone()),
+                        checksum,
+                    }.to_bytes();
+                    assert_eq!(expected, actual);
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn clone_eq() {
+        use Icmpv4Type::*;
+        let header = Icmpv4Header {
+            icmp_type: ParameterProblem(ParameterProblemHeader::BadLength),
+            checksum: 0,
+        };
+        assert_eq!(header.clone(), header);
+    }
+
+    #[test]
+    fn debug() {
+        use Icmpv4Type::*;
+        let header = Icmpv4Header {
+            icmp_type: ParameterProblem(ParameterProblemHeader::BadLength),
+            checksum: 0,
+        };
+        assert_eq!(
+            format!("{:?}", header),
+            format!(
+                "Icmpv4Header {{ icmp_type: {:?}, checksum: {:?} }}",
+                header.icmp_type, header.checksum
+            )
+        );
+    }
+}
diff --git a/src/transport/icmpv4_slice.rs b/src/transport/icmpv4_slice.rs
new file mode 100644
index 0000000..391cd8f
--- /dev/null
+++ b/src/transport/icmpv4_slice.rs
@@ -0,0 +1,905 @@
+use crate::{icmpv4::*, *};
+
+/// A slice containing an ICMPv4 network package.
+///
+/// Struct allows the selective read of fields in the ICMPv4
+/// packet.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Icmpv4Slice<'a> {
+    pub(crate) slice: &'a [u8],
+}
+
+impl<'a> Icmpv4Slice<'a> {
+    /// Creates a slice containing an ICMPv4 packet.
+    ///
+    /// # Errors
+    ///
+    /// The function will return an `Err` `err::LenError`
+    /// if the given slice is too small or does not match the expected
+    /// length in case of a timestamp message.
+    #[inline]
+    pub fn from_slice(slice: &'a [u8]) -> Result<Icmpv4Slice<'a>, err::LenError> {
+        // check length
+        if slice.len() < Icmpv4Header::MIN_LEN {
+            return Err(err::LenError {
+                required_len: Icmpv4Header::MIN_LEN,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::Icmpv4,
+                layer_start_offset: 0,
+            });
+        }
+
+        // SAFETY:
+        // Safe as it is previously checked that the slice has
+        // at least the length of Icmpv4Header::MIN_LEN (8).
+        let icmp_type: u8 = unsafe { *slice.get_unchecked(0) };
+        let icmp_code: u8 = unsafe { *slice.get_unchecked(1) };
+
+        // check type specific length
+        match icmp_type {
+            TYPE_TIMESTAMP => {
+                if 0 == icmp_code && TimestampMessage::LEN != slice.len() {
+                    return Err(err::LenError {
+                        required_len: TimestampMessage::LEN,
+                        len: slice.len(),
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::Icmpv4Timestamp,
+                        layer_start_offset: 0,
+                    });
+                }
+            }
+            TYPE_TIMESTAMP_REPLY => {
+                if 0 == icmp_code && TimestampMessage::LEN != slice.len() {
+                    return Err(err::LenError {
+                        required_len: TimestampMessage::LEN,
+                        len: slice.len(),
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::Icmpv4TimestampReply,
+                        layer_start_offset: 0,
+                    });
+                }
+            }
+            _ => {}
+        }
+
+        //done
+        Ok(Icmpv4Slice { slice })
+    }
+
+    /// Decode the header values into an [`Icmpv4Header`] struct.
+    #[inline]
+    pub fn header(&self) -> Icmpv4Header {
+        let icmp_type = self.icmp_type();
+        Icmpv4Header {
+            icmp_type,
+            checksum: self.checksum(),
+        }
+    }
+
+    /// Number of bytes/octets that will be converted into a
+    /// [`Icmpv4Header`] when [`Icmpv4Slice::header`] gets called.
+    #[inline]
+    pub fn header_len(&self) -> usize {
+        match self.type_u8() {
+            TYPE_TIMESTAMP | TYPE_TIMESTAMP_REPLY => {
+                if 0 == self.code_u8() {
+                    TimestampMessage::LEN
+                } else {
+                    8
+                }
+            }
+            _ => 8,
+        }
+    }
+
+    /// Decode the header values (excluding the checksum) into an [`Icmpv4Type`] enum.
+    pub fn icmp_type(&self) -> Icmpv4Type {
+        use Icmpv4Type::*;
+
+        unsafe fn timestamp_message(ptr: *const u8) -> TimestampMessage {
+            TimestampMessage {
+                id: get_unchecked_be_u16(ptr.add(4)),
+                seq: get_unchecked_be_u16(ptr.add(6)),
+                originate_timestamp: get_unchecked_be_u32(ptr.add(8)),
+                receive_timestamp: get_unchecked_be_u32(ptr.add(12)),
+                transmit_timestamp: get_unchecked_be_u32(ptr.add(16)),
+            }
+        }
+
+        match self.type_u8() {
+            TYPE_ECHO_REPLY => {
+                if 0 == self.code_u8() {
+                    return EchoReply(IcmpEchoHeader::from_bytes(self.bytes5to8()));
+                }
+            }
+            TYPE_DEST_UNREACH => {
+                use DestUnreachableHeader::*;
+                match self.code_u8() {
+                    CODE_DST_UNREACH_NET => return DestinationUnreachable(Network),
+                    CODE_DST_UNREACH_HOST => return DestinationUnreachable(Host),
+                    CODE_DST_UNREACH_PROTOCOL => return DestinationUnreachable(Protocol),
+                    CODE_DST_UNREACH_PORT => return DestinationUnreachable(Port),
+                    CODE_DST_UNREACH_NEED_FRAG => {
+                        return DestinationUnreachable(FragmentationNeeded {
+                            // SAFETY:
+                            // Safe as the contructor checks that the slice has
+                            // at least the length of Icmpv4Header::MIN_LEN (8).
+                            next_hop_mtu: unsafe {
+                                get_unchecked_be_u16(self.slice.as_ptr().add(6))
+                            },
+                        });
+                    }
+                    CODE_DST_UNREACH_SOURCE_ROUTE_FAILED => {
+                        return DestinationUnreachable(SourceRouteFailed)
+                    }
+                    CODE_DST_UNREACH_NET_UNKNOWN => return DestinationUnreachable(NetworkUnknown),
+                    CODE_DST_UNREACH_HOST_UNKNOWN => return DestinationUnreachable(HostUnknown),
+                    CODE_DST_UNREACH_ISOLATED => return DestinationUnreachable(Isolated),
+                    CODE_DST_UNREACH_NET_PROHIB => {
+                        return DestinationUnreachable(NetworkProhibited)
+                    }
+                    CODE_DST_UNREACH_HOST_PROHIB => return DestinationUnreachable(HostProhibited),
+                    CODE_DST_UNREACH_TOS_NET => return DestinationUnreachable(TosNetwork),
+                    CODE_DST_UNREACH_TOS_HOST => return DestinationUnreachable(TosHost),
+                    CODE_DST_UNREACH_FILTER_PROHIB => {
+                        return DestinationUnreachable(FilterProhibited)
+                    }
+                    CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION => {
+                        return DestinationUnreachable(HostPrecedenceViolation)
+                    }
+                    CODE_DST_UNREACH_PRECEDENCE_CUTOFF => {
+                        return DestinationUnreachable(PrecedenceCutoff)
+                    }
+                    _ => {}
+                }
+            }
+            TYPE_REDIRECT => {
+                use RedirectCode::*;
+                let code = match self.code_u8() {
+                    CODE_REDIRECT_FOR_NETWORK => Some(RedirectForNetwork),
+                    CODE_REDIRECT_FOR_HOST => Some(RedirectForHost),
+                    CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK => {
+                        Some(RedirectForTypeOfServiceAndNetwork)
+                    }
+                    CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST => Some(RedirectForTypeOfServiceAndHost),
+                    _ => None,
+                };
+                if let Some(code) = code {
+                    return Redirect(RedirectHeader {
+                        code,
+                        gateway_internet_address: self.bytes5to8(),
+                    });
+                }
+            }
+            TYPE_ECHO_REQUEST => {
+                if 0 == self.code_u8() {
+                    return EchoRequest(IcmpEchoHeader::from_bytes(self.bytes5to8()));
+                }
+            }
+            TYPE_TIME_EXCEEDED => {
+                use TimeExceededCode::*;
+                match self.code_u8() {
+                    CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT => {
+                        return TimeExceeded(TtlExceededInTransit);
+                    }
+                    CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED => {
+                        return TimeExceeded(FragmentReassemblyTimeExceeded);
+                    }
+                    _ => {}
+                }
+            }
+            TYPE_PARAMETER_PROBLEM => {
+                use ParameterProblemHeader::*;
+                match self.code_u8() {
+                    CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR => {
+                        return ParameterProblem(PointerIndicatesError(
+                            // SAFETY:
+                            // Safe as the contructor checks that the slice has
+                            // at least the length of Icmpv4Header::MIN_LEN (8).
+                            unsafe { *self.slice.get_unchecked(4) },
+                        ));
+                    }
+                    CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION => {
+                        return ParameterProblem(MissingRequiredOption);
+                    }
+                    CODE_PARAMETER_PROBLEM_BAD_LENGTH => {
+                        return ParameterProblem(BadLength);
+                    }
+                    _ => {}
+                }
+            }
+            TYPE_TIMESTAMP => {
+                if 0 == self.code_u8() {
+                    // SAFETY:
+                    // Safe as the contructor checks that the slice has
+                    // the length of TimestampMessage::SERIALIZED_SIZE (20).
+                    unsafe {
+                        return TimestampRequest(timestamp_message(self.slice.as_ptr()));
+                    }
+                }
+            }
+            TYPE_TIMESTAMP_REPLY => {
+                if 0 == self.code_u8() {
+                    // SAFETY:
+                    // Safe as the contructor checks that the slice has
+                    // the length of TimestampMessage::SERIALIZED_SIZE (20).
+                    unsafe {
+                        return TimestampReply(timestamp_message(self.slice.as_ptr()));
+                    }
+                }
+            }
+            _ => {}
+        }
+
+        Unknown {
+            type_u8: self.type_u8(),
+            code_u8: self.code_u8(),
+            bytes5to8: self.bytes5to8(),
+        }
+    }
+
+    /// Returns "type" value in the ICMPv4 header.
+    #[inline]
+    pub fn type_u8(&self) -> u8 {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of Icmpv4Header::MIN_LEN (8).
+        unsafe { *self.slice.get_unchecked(0) }
+    }
+
+    /// Returns "code" value in the ICMPv4 header.
+    #[inline]
+    pub fn code_u8(&self) -> u8 {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of Icmpv4Header::MIN_LEN (8).
+        unsafe { *self.slice.get_unchecked(1) }
+    }
+
+    /// Returns "checksum" value in the ICMPv4 header.
+    #[inline]
+    pub fn checksum(&self) -> u16 {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of Icmpv4Header::MIN_LEN (8).
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) }
+    }
+
+    /// Returns the bytes from position 4 till and including the 8th position
+    /// in the ICMPv4 header.
+    ///
+    /// These bytes located at th 5th, 6th, 7th and 8th position of the ICMP
+    /// packet can depending on the ICMPv4 type and code contain additional data.
+    #[inline]
+    pub fn bytes5to8(&self) -> [u8; 4] {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of Icmpv4Header::MIN_LEN (8).
+        unsafe {
+            [
+                *self.slice.get_unchecked(4),
+                *self.slice.get_unchecked(5),
+                *self.slice.get_unchecked(6),
+                *self.slice.get_unchecked(7),
+            ]
+        }
+    }
+
+    /// Returns a slice to the bytes not covered by `.header()`.
+    ///
+    /// The contents of the slice returned by `payload()` depends on the type
+    /// and code of the ICMP packet:
+    ///
+    /// | `.header().icmp_type` or `.icmp_type()`                                                                                                    | Payload Content                                                              |
+    /// |--------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|
+    /// | [`Icmpv4Type::EchoReply`]<br>[`Icmpv4Type::EchoRequest`]<br>                                                                               | Data part of the echo message                                                |
+    /// | [`Icmpv4Type::DestinationUnreachable`]<br>[`Icmpv4Type::Redirect`]<br>[`Icmpv4Type::TimeExceeded`]<br>[`Icmpv4Type::ParameterProblem`]<br> | Internet Header + 64 bits of Original Data Datagram causing the ICMP message |
+    /// | [`Icmpv4Type::TimestampRequest`]<br>[`Icmpv4Type::TimestampReply`]<br>                                                                     | Nothing                                                                      |
+    /// | [`Icmpv4Type::Unknown`]                                                                                                                    | Everything after the 8th byte/octet of the ICMP packet.                      |
+    #[inline]
+    pub fn payload(&self) -> &'a [u8] {
+        // explicitly inlined the code to determine the
+        // length of the payload to make the cecking of the
+        // usafe code easier.
+        let header_len = match self.type_u8() {
+            // SAFETY:
+            // Length safe as the contructor checks that the slice has
+            // the length of TimestampMessage::SERIALIZED_SIZE (20)
+            // for the messages types TYPE_TIMESTAMP and TYPE_TIMESTAMP_REPLY.
+            TYPE_TIMESTAMP | TYPE_TIMESTAMP_REPLY => {
+                if 0 == self.code_u8() {
+                    TimestampMessage::LEN
+                } else {
+                    8
+                }
+            }
+            // SAFETY:
+            // Length safe as the contructor checks that the slice has
+            // at least the length of Icmpv4Header::MIN_LEN(8) for
+            // all message types.
+            _ => 8,
+        };
+        // SAFETY:
+        // Lengths have been depending on type in the constructor of the
+        // ICMPv4Slice.
+        unsafe {
+            core::slice::from_raw_parts(
+                self.slice.as_ptr().add(header_len),
+                self.slice.len() - header_len,
+            )
+        }
+    }
+
+    /// Returns the slice containing the ICMPv4 packet.
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    #[test]
+    fn from_slice() {
+        // normal case
+        {
+            let bytes = [0u8; 8];
+            let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
+            assert_eq!(slice.slice(), &bytes);
+        }
+
+        // smaller then min size error
+        for bad_len in 0..8 {
+            let bytes = [0u8; 8];
+            assert_eq!(
+                Icmpv4Slice::from_slice(&bytes[..bad_len]).unwrap_err(),
+                err::LenError {
+                    required_len: Icmpv4Header::MIN_LEN,
+                    len: bad_len,
+                    len_source: LenSource::Slice,
+                    layer: err::Layer::Icmpv4,
+                    layer_start_offset: 0,
+                }
+            );
+        }
+
+        // timestamp tests
+        for ts_type_u8 in [TYPE_TIMESTAMP, TYPE_TIMESTAMP_REPLY] {
+            let bytes = {
+                let mut bytes = [0u8; 26];
+                bytes[0] = ts_type_u8;
+                bytes
+            };
+
+            // valid timestamps
+            {
+                let slice = Icmpv4Slice::from_slice(&bytes[..20]).unwrap();
+                assert_eq!(slice.slice(), &bytes[..20]);
+            }
+
+            // too short timestamps
+            for bad_len in 8..20 {
+                assert_eq!(
+                    Icmpv4Slice::from_slice(&bytes[..bad_len]).unwrap_err(),
+                    err::LenError {
+                        required_len: TimestampMessage::LEN,
+                        len: bad_len,
+                        len_source: LenSource::Slice,
+                        layer: if ts_type_u8 == TYPE_TIMESTAMP {
+                            err::Layer::Icmpv4Timestamp
+                        } else {
+                            err::Layer::Icmpv4TimestampReply
+                        },
+                        layer_start_offset: 0,
+                    }
+                );
+            }
+
+            // too large timestamps
+            for bad_len in 21..26 {
+                assert_eq!(
+                    Icmpv4Slice::from_slice(&bytes[..bad_len]).unwrap_err(),
+                    err::LenError {
+                        required_len: TimestampMessage::LEN,
+                        len: bad_len,
+                        len_source: LenSource::Slice,
+                        layer: if ts_type_u8 == TYPE_TIMESTAMP {
+                            err::Layer::Icmpv4Timestamp
+                        } else {
+                            err::Layer::Icmpv4TimestampReply
+                        },
+                        layer_start_offset: 0,
+                    }
+                );
+            }
+
+            // timestamp with a non zero code
+            for code_u8 in 1..=u8::MAX {
+                let mut bytes = [0u8; 20];
+                bytes[0] = ts_type_u8;
+                bytes[1] = code_u8;
+                let slice = Icmpv4Slice::from_slice(&bytes[..8]).unwrap();
+                assert_eq!(slice.slice(), &bytes[..8]);
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header(bytes in any::<[u8;20]>()) {
+            let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
+            assert_eq!(
+                Icmpv4Header {
+                    icmp_type: slice.icmp_type(),
+                    checksum: slice.checksum(),
+                },
+                slice.header()
+            );
+        }
+    }
+
+    #[test]
+    fn header_len() {
+        use Icmpv4Type::*;
+        let dummy_ts = TimestampMessage {
+            id: 0,
+            seq: 0,
+            originate_timestamp: 0,
+            receive_timestamp: 0,
+            transmit_timestamp: 0,
+        };
+        let dummy_echo = IcmpEchoHeader { id: 0, seq: 0 };
+        let dummy_redirect = RedirectHeader {
+            code: RedirectCode::RedirectForNetwork,
+            gateway_internet_address: [0; 4],
+        };
+        let tests = [
+            (Unknown {
+                type_u8: u8::MAX,
+                code_u8: 0,
+                bytes5to8: [0; 4],
+            }),
+            (EchoReply(dummy_echo)),
+            (DestinationUnreachable(DestUnreachableHeader::Network)),
+            (Redirect(dummy_redirect)),
+            (EchoRequest(dummy_echo)),
+            (TimeExceeded(TimeExceededCode::TtlExceededInTransit)),
+            (ParameterProblem(ParameterProblemHeader::BadLength)),
+            (TimestampRequest(dummy_ts.clone())),
+            // check that a non zero code value return 8
+            (Unknown {
+                type_u8: TYPE_TIMESTAMP,
+                code_u8: 1,
+                bytes5to8: [0; 4],
+            }),
+            (TimestampReply(dummy_ts)),
+            // check that a non zero code value return 8
+            (Unknown {
+                type_u8: TYPE_TIMESTAMP_REPLY,
+                code_u8: 1,
+                bytes5to8: [0; 4],
+            }),
+        ];
+        for t in tests {
+            assert_eq!(
+                t.header_len(),
+                Icmpv4Slice::from_slice(&Icmpv4Header::new(t).to_bytes())
+                    .unwrap()
+                    .header_len()
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn icmp_type(base_bytes in any::<[u8;20]>()) {
+
+            use Icmpv4Type::*;
+
+            let gen_bytes = |type_u8: u8, code_u8: u8| -> [u8;20] {
+                let mut bytes = base_bytes;
+                bytes[0] = type_u8;
+                bytes[1] = code_u8;
+                bytes
+            };
+
+            let assert_unknown = |type_u8: u8, code_u8: u8| {
+                let bytes = gen_bytes(type_u8, code_u8);
+                let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
+                assert_eq!(
+                    slice.icmp_type(),
+                    Unknown{
+                        type_u8,
+                        code_u8,
+                        bytes5to8: slice.bytes5to8(),
+                    }
+                );
+            };
+
+            // unknown types
+            for type_u8 in 0..=u8::MAX{
+                match type_u8 {
+                    TYPE_ECHO_REPLY | TYPE_DEST_UNREACH | TYPE_REDIRECT |
+                    TYPE_ECHO_REQUEST | TYPE_TIME_EXCEEDED | TYPE_PARAMETER_PROBLEM |
+                    TYPE_TIMESTAMP | TYPE_TIMESTAMP_REPLY => {},
+                    type_u8 => {
+                        assert_unknown(type_u8, base_bytes[1]);
+                    }
+                }
+            }
+
+            // echo reply
+            {
+                // matching code
+                {
+                    let bytes = gen_bytes(TYPE_ECHO_REPLY, 0);
+                    let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
+                    assert_eq!(
+                        slice.icmp_type(),
+                        EchoReply(IcmpEchoHeader::from_bytes(slice.bytes5to8()))
+                    );
+                }
+
+                // unknown code
+                for unknow_code in 1..=u8::MAX {
+                    assert_unknown(TYPE_ECHO_REPLY, unknow_code);
+                }
+            }
+
+            // destination unreachable
+            {
+                use DestUnreachableHeader::*;
+                // trivial code values
+                {
+                    let trivial_tests = [
+                        (CODE_DST_UNREACH_NET, Network),
+                        (CODE_DST_UNREACH_HOST, Host),
+                        (CODE_DST_UNREACH_PROTOCOL, Protocol),
+                        (CODE_DST_UNREACH_PORT, Port),
+                        // need frag skipped as contains an additional value
+                        (CODE_DST_UNREACH_SOURCE_ROUTE_FAILED, SourceRouteFailed),
+                        (CODE_DST_UNREACH_NET_UNKNOWN, NetworkUnknown),
+                        (CODE_DST_UNREACH_HOST_UNKNOWN, HostUnknown),
+                        (CODE_DST_UNREACH_ISOLATED, Isolated),
+                        (CODE_DST_UNREACH_NET_PROHIB, NetworkProhibited),
+                        (CODE_DST_UNREACH_HOST_PROHIB, HostProhibited),
+                        (CODE_DST_UNREACH_TOS_NET, TosNetwork),
+                        (CODE_DST_UNREACH_TOS_HOST, TosHost),
+                        (CODE_DST_UNREACH_FILTER_PROHIB, FilterProhibited),
+                        (CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION, HostPrecedenceViolation),
+                        (CODE_DST_UNREACH_PRECEDENCE_CUTOFF, PrecedenceCutoff),
+                    ];
+
+                    for (code_u8, expected) in trivial_tests {
+                        let bytes = gen_bytes(TYPE_DEST_UNREACH, code_u8);
+                        let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
+                        assert_eq!(
+                            slice.icmp_type(),
+                            DestinationUnreachable(expected)
+                        );
+                    }
+                }
+
+                // need frag
+                {
+                    let bytes = gen_bytes(TYPE_DEST_UNREACH, CODE_DST_UNREACH_NEED_FRAG);
+                    let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
+                    assert_eq!(
+                        slice.icmp_type(),
+                        DestinationUnreachable(FragmentationNeeded {
+                            next_hop_mtu: u16::from_be_bytes([bytes[6], bytes[7]])
+                        })
+                    );
+                }
+
+                // unknown codes
+                for unknow_code in 16..=u8::MAX {
+                    assert_unknown(TYPE_ECHO_REPLY, unknow_code);
+                }
+            }
+
+            // redirect
+            {
+                use RedirectCode::*;
+                // known codes
+                {
+                    let trivial_tests = [
+                        (CODE_REDIRECT_FOR_NETWORK, RedirectForNetwork),
+                        (CODE_REDIRECT_FOR_HOST, RedirectForHost),
+                        (CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK, RedirectForTypeOfServiceAndNetwork),
+                        (CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST, RedirectForTypeOfServiceAndHost),
+                    ];
+
+                    for (code_u8, expected) in trivial_tests {
+                        let bytes = gen_bytes(TYPE_REDIRECT, code_u8);
+                        let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
+                        assert_eq!(
+                            slice.icmp_type(),
+                            Redirect(RedirectHeader{
+                                code: expected,
+                                gateway_internet_address: slice.bytes5to8(),
+                            })
+                        );
+                    }
+                }
+
+                // unknown codes
+                for unknow_code in 4..=u8::MAX {
+                    assert_unknown(TYPE_REDIRECT, unknow_code);
+                }
+            }
+
+            // echo request
+            {
+                // matching code
+                {
+                    let bytes = gen_bytes(TYPE_ECHO_REQUEST, 0);
+                    let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
+                    assert_eq!(
+                        slice.icmp_type(),
+                        EchoRequest(IcmpEchoHeader::from_bytes(slice.bytes5to8()))
+                    );
+                }
+
+                // unknown code
+                for unknow_code in 1..=u8::MAX {
+                    assert_unknown(TYPE_ECHO_REQUEST, unknow_code);
+                }
+            }
+
+            // time exceeded
+            {
+                use TimeExceededCode::*;
+                // known codes
+                {
+                    let trivial_tests = [
+                        (CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT, TtlExceededInTransit),
+                        (CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED, FragmentReassemblyTimeExceeded),
+                    ];
+
+                    for (code_u8, expected) in trivial_tests {
+                        let bytes = gen_bytes(TYPE_TIME_EXCEEDED, code_u8);
+                        let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
+                        assert_eq!(
+                            slice.icmp_type(),
+                            TimeExceeded(expected)
+                        );
+                    }
+                }
+
+                // unknown code
+                for unknow_code in 2..=u8::MAX {
+                    assert_unknown(TYPE_TIME_EXCEEDED, unknow_code);
+                }
+            }
+
+            // parameter porblem
+            {
+                use ParameterProblemHeader::*;
+                // trivial code values
+                {
+                    let trivial_tests = [
+                        (CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION, MissingRequiredOption),
+                        (CODE_PARAMETER_PROBLEM_BAD_LENGTH, BadLength),
+                    ];
+
+                    for (code_u8, expected) in trivial_tests {
+                        let bytes = gen_bytes(TYPE_PARAMETER_PROBLEM, code_u8);
+                        let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
+                        assert_eq!(
+                            slice.icmp_type(),
+                            ParameterProblem(expected)
+                        );
+                    }
+                }
+
+                // with pointer
+                {
+                    let bytes = gen_bytes(TYPE_PARAMETER_PROBLEM, CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR);
+                    let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
+                    assert_eq!(
+                        slice.icmp_type(),
+                        ParameterProblem(PointerIndicatesError(bytes[4]))
+                    );
+                }
+
+                // unknown codes
+                for unknow_code in 3..=u8::MAX {
+                    assert_unknown(TYPE_PARAMETER_PROBLEM, unknow_code);
+                }
+            }
+
+            // timestamp
+            {
+                // matching code
+                {
+                    let bytes = gen_bytes(TYPE_TIMESTAMP, 0);
+                    let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
+                    assert_eq!(
+                        slice.icmp_type(),
+                        TimestampRequest(TimestampMessage::from_bytes([
+                            bytes[4], bytes[5], bytes[6], bytes[7],
+                            bytes[8], bytes[9], bytes[10], bytes[11],
+                            bytes[12], bytes[13], bytes[14], bytes[15],
+                            bytes[16], bytes[17], bytes[18], bytes[19],
+                        ]))
+                    );
+                }
+
+                // unknown code
+                for unknow_code in 1..=u8::MAX {
+                    assert_unknown(TYPE_TIMESTAMP, unknow_code);
+                }
+            }
+
+            // timestamp reply
+            {
+                // matching code
+                {
+                    let bytes = gen_bytes(TYPE_TIMESTAMP_REPLY, 0);
+                    let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
+                    assert_eq!(
+                        slice.icmp_type(),
+                        TimestampReply(TimestampMessage::from_bytes([
+                            bytes[4], bytes[5], bytes[6], bytes[7],
+                            bytes[8], bytes[9], bytes[10], bytes[11],
+                            bytes[12], bytes[13], bytes[14], bytes[15],
+                            bytes[16], bytes[17], bytes[18], bytes[19],
+                        ]))
+                    );
+                }
+
+                // unknown code
+                for unknow_code in 1..=u8::MAX {
+                    assert_unknown(TYPE_TIMESTAMP_REPLY, unknow_code);
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn type_u8(bytes in any::<[u8;20]>()) {
+            assert_eq!(
+                bytes[0],
+                Icmpv4Slice::from_slice(&bytes).unwrap().type_u8(),
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn code_u8(bytes in any::<[u8;20]>()) {
+            assert_eq!(
+                bytes[1],
+                Icmpv4Slice::from_slice(&bytes).unwrap().code_u8(),
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn checksum(bytes in any::<[u8;20]>()) {
+            assert_eq!(
+                u16::from_be_bytes([bytes[2], bytes[3]]),
+                Icmpv4Slice::from_slice(&bytes).unwrap().checksum(),
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn bytes5to8(bytes in any::<[u8;20]>()) {
+            assert_eq!(
+                [bytes[4], bytes[5], bytes[6], bytes[7]],
+                Icmpv4Slice::from_slice(&bytes).unwrap().bytes5to8(),
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn payload(
+            payload in proptest::collection::vec(any::<u8>(), 8..26)
+        ) {
+            use Icmpv4Type::*;
+            let dummy_ts = TimestampMessage{
+                id: 0,
+                seq: 0,
+                originate_timestamp: 0,
+                receive_timestamp: 0,
+                transmit_timestamp: 0,
+            };
+            let dummy_echo = IcmpEchoHeader{
+                id: 0,
+                seq: 0,
+            };
+            let dummy_redirect = RedirectHeader{
+                code: RedirectCode::RedirectForNetwork,
+                gateway_internet_address: [0;4],
+            };
+            // tests with variable payloads
+            {
+                let var_tests = [
+                    Unknown{type_u8: 0, code_u8: 0, bytes5to8: [0;4]},
+                    EchoReply(dummy_echo),
+                    DestinationUnreachable(DestUnreachableHeader::Network),
+                    Redirect(dummy_redirect),
+                    EchoRequest(dummy_echo),
+                    TimeExceeded(TimeExceededCode::TtlExceededInTransit),
+                    ParameterProblem(ParameterProblemHeader::BadLength),
+                    // timestamps with non-zero code values
+                    Unknown{type_u8: TYPE_TIMESTAMP, code_u8: 1, bytes5to8: [0;4]},
+                    Unknown{type_u8: TYPE_TIMESTAMP_REPLY, code_u8: 1, bytes5to8: [0;4]},
+                ];
+                for t in var_tests {
+
+                    let mut bytes = Vec::with_capacity(t.header_len() + payload.len());
+                    Icmpv4Header::new(t.clone()).write(&mut bytes).unwrap();
+                    bytes.extend_from_slice(&payload);
+
+                    assert_eq!(
+                        &payload[..],
+                        Icmpv4Slice::from_slice(&bytes).unwrap().payload()
+                    );
+                }
+            }
+            // tests with fixed payload sizes
+            {
+                let fixed_tests = [
+                    (0, TimestampRequest(dummy_ts.clone())),
+                    (0, TimestampReply(dummy_ts)),
+                ];
+                for t in fixed_tests {
+                    let mut bytes = Vec::with_capacity(t.1.header_len() + t.0);
+                    Icmpv4Header::new(t.1.clone()).write(&mut bytes).unwrap();
+                    bytes.extend_from_slice(&payload[..t.0]);
+
+                    assert_eq!(
+                        &payload[..t.0],
+                        Icmpv4Slice::from_slice(&bytes).unwrap().payload()
+                    );
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn slice(bytes in proptest::collection::vec(any::<u8>(), 20..1024)) {
+            let slice = if bytes[0] == TYPE_TIMESTAMP || bytes[0] == TYPE_TIMESTAMP_REPLY {
+                &bytes[..20]
+            } else {
+                &bytes[..]
+            };
+            assert_eq!(
+                slice,
+                Icmpv4Slice::from_slice(slice).unwrap().slice(),
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(bytes in any::<[u8;20]>()) {
+            let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
+            assert_eq!(slice, slice.clone());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn debug(bytes in any::<[u8;20]>()) {
+            let slice = Icmpv4Slice::from_slice(&bytes).unwrap();
+            assert_eq!(
+                format!("{:?}", slice),
+                format!("Icmpv4Slice {{ slice: {:?} }}", &bytes[..])
+            );
+        }
+    }
+}
diff --git a/src/transport/icmpv4_type.rs b/src/transport/icmpv4_type.rs
new file mode 100644
index 0000000..307814f
--- /dev/null
+++ b/src/transport/icmpv4_type.rs
@@ -0,0 +1,599 @@
+use crate::*;
+
+/// Starting contents of an ICMPv4 packet without the checksum.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum Icmpv4Type {
+    /// In case of an unknown ICMP type and code combination is received the
+    /// header elements are stored raw in this enum value. The `Unknown` value can
+    /// also be passed to the `Icmpv4Header::write` function to write arbitrary ICMP
+    /// packets.
+    ///
+    /// # What is part of the header for `Icmpv4Type::Unknown`?
+    ///
+    /// For unknown ICMP type & code combination the first 8 bytes are stored
+    /// in the [`Icmpv4Header`] and the rest is stored in the payload
+    /// ([`Icmpv4Slice::payload`] or [`PacketHeaders::payload`]).
+    ///
+    ///
+    /// ```text
+    /// 0               1               2               3               4
+    /// +---------------------------------------------------------------+  -
+    /// |     type_u8   |    code_u8    |  checksum (in Icmpv4Header)   |  |
+    /// +---------------------------------------------------------------+  | part of header & type
+    /// |                          bytes5to8                            |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// |                                                               |  |
+    /// ...                           ...                             ...  | part of payload
+    /// |                                                               |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// ```
+    Unknown {
+        /// ICMP type (present in the first byte of the ICMP packet).
+        type_u8: u8,
+        /// ICMP code (present in the 2nd byte of the ICMP packet).
+        code_u8: u8,
+        /// Bytes located at th 5th, 6th, 7th and 8th position of the ICMP packet.
+        bytes5to8: [u8; 4],
+    },
+
+    /// Response to an `EchoRequest` message (defined in RFC792).
+    ///
+    /// # What is part of the header for `Icmpv4Type::EchoReply`?
+    ///
+    /// For the [`Icmpv4Type::EchoReply`] type the first 8 bytes/octets of the
+    /// ICMP packet are part of the header. This includes the `id` and `seq`
+    /// fields. The data part of the ICMP Echo Reply packet is part of the
+    /// payload ([`Icmpv4Slice::payload`] or [`PacketHeaders::payload`])
+    /// and not part of the [`Icmpv4Header`].
+    ///
+    /// ```text
+    /// 0               1               2               3               4
+    /// +---------------------------------------------------------------+  -
+    /// |       0       |       0       |  checksum (in Icmpv4Header)   |  |
+    /// +---------------------------------------------------------------+  | part of header & type
+    /// |          [value].id           |         [value].seq           |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// |                                                               |  |
+    /// ...                          <data>                           ...  | part of payload
+    /// |                                                               |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// ```
+    EchoReply(IcmpEchoHeader),
+
+    /// Message sent to inform the client that the destination is unreachable for
+    /// some reason (defined in RFC792).
+    ///
+    /// # What is part of the header for `Icmpv4Type::DestinationUnreachable`?
+    ///
+    /// For the [`Icmpv4Type::DestinationUnreachable`] type the first 8 bytes/octets
+    /// of the ICMP packet are part of the header. This includes the `next_hop_mtu`
+    /// field. The `unused` part is not stored and droped. The offending packet
+    /// is stored in the payload part of the packet ([`Icmpv4Slice::payload`] or
+    /// [`PacketHeaders::payload`]) and is not part of the [`Icmpv4Header`].
+    ///
+    /// ```text
+    /// 0               1               2               3               4
+    /// +---------------------------------------------------------------+  -
+    /// |       3       |       0       |  checksum (in Icmpv4Header)   |  |
+    /// +---------------------------------------------------------------+  | part of header & type
+    /// |[v].next_hop...|                    <unused>                   |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// |                                                               |  |
+    /// ...    Internet Header + 64 bits of Original Data Datagram    ...  | part of payload
+    /// |                                                               |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// ```
+    DestinationUnreachable(icmpv4::DestUnreachableHeader),
+
+    /// Requests data packets be sent on an alternative route (defined in RFC792).
+    ///
+    /// # What is part of the header for `Icmpv4Type::Redirect`?
+    ///
+    /// For the [`Icmpv4Type::Redirect`] type the first 8 bytes/octets of the ICMP
+    /// packet are part of the header. This includes the `gateway_internet_address`
+    /// field. The offending packet is stored in the payload part of the packet
+    /// ([`Icmpv4Slice::payload`] or [`PacketHeaders::payload`]) and is not part of
+    /// the [`Icmpv4Header`].
+    ///
+    /// ```text
+    /// 0               1               2               3               4
+    /// +---------------------------------------------------------------+  -
+    /// |       5       | [value].code  |  checksum (in Icmpv4Header)   |  |
+    /// +---------------------------------------------------------------+  | part of header & type
+    /// |                [value].gateway_internet_address               |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// |                                                               |  |
+    /// ..     Internet Header + 64 bits of Original Data Datagram    ...  | part of payload
+    /// |                                                               |  ↓
+    /// +---------------------------------------------------------------+  -
+    Redirect(icmpv4::RedirectHeader),
+
+    /// Requesting an `EchoReply` from the receiver (defined in RFC792)
+    ///
+    /// # What is part of the header for `Icmpv4Type::EchoRequest`?
+    ///
+    /// For the [`Icmpv4Type::EchoRequest`] type the first 8 bytes/octets of the
+    /// ICMP packet are part of the header. This includes the `id` and `seq`
+    /// fields. The data part of the ICMP echo request packet is part of the payload
+    /// ([`Icmpv4Slice::payload`] & [`PacketHeaders::payload`]) and not part of the
+    /// [`Icmpv4Header`].
+    ///
+    /// ```text
+    /// 0               1               2               3               4
+    /// +---------------------------------------------------------------+  -
+    /// |       8       |       0       |  checksum (in Icmpv4Header)   |  |
+    /// +---------------------------------------------------------------+  | part of header & type
+    /// |          [value].id           |         [value].seq           |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// |                                                               |  |
+    /// ...                          <data>                           ...  | part of payload
+    /// |                                                               |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// ```
+    EchoRequest(IcmpEchoHeader),
+
+    /// Generated when a datagram had to be discarded due to the time to live field
+    /// reaching zero (defined in RFC792).
+    ///
+    /// # What is part of the header for `Icmpv4Type::TimeExceeded`?
+    ///
+    /// For the `Icmpv4Type::TimeExceeded` type the first 8 bytes/octets of the ICMP
+    /// packet are part of the header. The `unused` part is not stored and droped.
+    /// The offending packet is stored in the payload part of the packet
+    /// ([`Icmpv4Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
+    /// the [`Icmpv4Header`].
+    ///
+    /// ```text
+    /// 0               1               2               3               4
+    /// +---------------------------------------------------------------+  -
+    /// |       11      | [value as u8] |  checksum (in Icmpv4Header)   |  |
+    /// +---------------------------------------------------------------+  | part of header & type
+    /// |                           <unused>                            |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// |                                                               |  |
+    /// ...    Internet Header + 64 bits of Original Data Datagram    ...  | part of payload
+    /// |                                                               |  ↓
+    /// +---------------------------------------------------------------+  -
+    TimeExceeded(icmpv4::TimeExceededCode),
+
+    /// Sent if there is a problem with a parameter in a received packet.
+    ///
+    /// # What is part of the header for `Icmpv4Type::ParameterProblem`?
+    ///
+    /// For the `Icmpv4Type::ParameterProblem` type the first 8 bytes/octets of the ICMP
+    /// packet are part of the header. The `unused` part is not stored and droped.
+    /// The offending packet is stored in the payload part of the packet
+    /// ([`Icmpv4Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
+    /// the [`Icmpv4Header`].
+    ///
+    /// ```text
+    /// 0               1               2               3               4
+    /// +---------------------------------------------------------------+  -
+    /// |       12      | [v].code_u8() |  checksum (in Icmpv4Header)   |  |
+    /// +---------------------------------------------------------------+  | part of header & type
+    /// |[value].pointer|                   <unused>                    |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// |                                                               |  |
+    /// ...    Internet Header + 64 bits of Original Data Datagram    ...  | part of payload
+    /// |                                                               |  ↓
+    /// +---------------------------------------------------------------+  -
+    ParameterProblem(icmpv4::ParameterProblemHeader),
+
+    /// Timestamp is used for time synchronization.
+    ///
+    /// # What is part of the header for `Icmpv4Type::TimestampRequest`?
+    ///
+    /// For the `Icmpv4Type::TimestampRequest` type the entire ICMP packet is
+    /// contained within the header. The payload data is empty.
+    TimestampRequest(icmpv4::TimestampMessage),
+
+    /// Anwser to a `TimestampRequest` message.
+    ///
+    /// # What is part of the header for `Icmpv4Type::TimestampReply`?
+    ///
+    /// For the `Icmpv4Type::TimestampReply` type the entire ICMP packet is
+    /// contained within the header. The payload data is empty.
+    TimestampReply(icmpv4::TimestampMessage),
+}
+
+impl Icmpv4Type {
+    /// Returns the length in bytes/octets of the header of
+    /// this ICMPv4 message type.
+    #[inline]
+    pub fn header_len(&self) -> usize {
+        use Icmpv4Type::*;
+        match self {
+            Unknown {
+                type_u8: _,
+                code_u8: _,
+                bytes5to8: _,
+            }
+            | EchoReply(_)
+            | DestinationUnreachable(_)
+            | Redirect(_)
+            | EchoRequest(_)
+            | TimeExceeded(_)
+            | ParameterProblem(_) => 8,
+            TimestampRequest(_) | TimestampReply(_) => icmpv4::TimestampMessage::LEN,
+        }
+    }
+
+    /// If the ICMP type has a fixed size returns the number of
+    /// bytes that should be present after the header of this type.
+    #[inline]
+    pub fn fixed_payload_size(&self) -> Option<usize> {
+        use Icmpv4Type::*;
+        match self {
+            Unknown {
+                type_u8: _,
+                code_u8: _,
+                bytes5to8: _,
+            }
+            | EchoReply(_)
+            | DestinationUnreachable(_)
+            | Redirect(_)
+            | EchoRequest(_)
+            | TimeExceeded(_)
+            | ParameterProblem(_) => None,
+            TimestampRequest(_) | TimestampReply(_) => Some(0),
+        }
+    }
+
+    /// Calculate the ICMP checksum value.
+    pub fn calc_checksum(&self, payload: &[u8]) -> u16 {
+        use crate::{icmpv4::*, Icmpv4Type::*};
+        match self {
+            Unknown {
+                type_u8,
+                code_u8,
+                bytes5to8,
+            } => checksum::Sum16BitWords::new()
+                .add_2bytes([*type_u8, *code_u8])
+                .add_4bytes(*bytes5to8),
+            EchoReply(header) => checksum::Sum16BitWords::new()
+                .add_2bytes([TYPE_ECHO_REPLY, 0])
+                .add_2bytes(header.id.to_be_bytes())
+                .add_2bytes(header.seq.to_be_bytes()),
+            DestinationUnreachable(ref header) => {
+                use DestUnreachableHeader::*;
+                match header {
+                    Network => checksum::Sum16BitWords::new()
+                        .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET]),
+                    Host => checksum::Sum16BitWords::new()
+                        .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST]),
+                    Protocol => checksum::Sum16BitWords::new()
+                        .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_PROTOCOL]),
+                    Port => checksum::Sum16BitWords::new()
+                        .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_PORT]),
+                    FragmentationNeeded { next_hop_mtu } => checksum::Sum16BitWords::new()
+                        .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_NEED_FRAG])
+                        .add_2bytes(next_hop_mtu.to_be_bytes()),
+                    SourceRouteFailed => checksum::Sum16BitWords::new()
+                        .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_SOURCE_ROUTE_FAILED]),
+                    NetworkUnknown => checksum::Sum16BitWords::new()
+                        .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET_UNKNOWN]),
+                    HostUnknown => checksum::Sum16BitWords::new()
+                        .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST_UNKNOWN]),
+                    Isolated => checksum::Sum16BitWords::new()
+                        .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_ISOLATED]),
+                    NetworkProhibited => checksum::Sum16BitWords::new()
+                        .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET_PROHIB]),
+                    HostProhibited => checksum::Sum16BitWords::new()
+                        .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST_PROHIB]),
+                    TosNetwork => checksum::Sum16BitWords::new()
+                        .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_TOS_NET]),
+                    TosHost => checksum::Sum16BitWords::new()
+                        .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_TOS_HOST]),
+                    FilterProhibited => checksum::Sum16BitWords::new()
+                        .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_FILTER_PROHIB]),
+                    HostPrecedenceViolation => checksum::Sum16BitWords::new().add_2bytes([
+                        TYPE_DEST_UNREACH,
+                        CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION,
+                    ]),
+                    PrecedenceCutoff => checksum::Sum16BitWords::new()
+                        .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_PRECEDENCE_CUTOFF]),
+                }
+            }
+            Redirect(header) => checksum::Sum16BitWords::new()
+                .add_2bytes([TYPE_REDIRECT, header.code as u8])
+                .add_4bytes(header.gateway_internet_address),
+            EchoRequest(header) => checksum::Sum16BitWords::new()
+                .add_2bytes([TYPE_ECHO_REQUEST, 0])
+                .add_2bytes(header.id.to_be_bytes())
+                .add_2bytes(header.seq.to_be_bytes()),
+            TimeExceeded(code) => {
+                checksum::Sum16BitWords::new().add_2bytes([TYPE_TIME_EXCEEDED, *code as u8])
+            }
+            ParameterProblem(header) => {
+                use ParameterProblemHeader::*;
+                match header {
+                    PointerIndicatesError(pointer) => checksum::Sum16BitWords::new()
+                        .add_2bytes([
+                            TYPE_PARAMETER_PROBLEM,
+                            CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR,
+                        ])
+                        .add_2bytes([*pointer, 0]),
+                    MissingRequiredOption => checksum::Sum16BitWords::new().add_2bytes([
+                        TYPE_PARAMETER_PROBLEM,
+                        CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION,
+                    ]),
+                    BadLength => checksum::Sum16BitWords::new()
+                        .add_2bytes([TYPE_PARAMETER_PROBLEM, CODE_PARAMETER_PROBLEM_BAD_LENGTH]),
+                }
+            }
+            TimestampRequest(msg) => checksum::Sum16BitWords::new()
+                .add_2bytes([TYPE_TIMESTAMP, 0])
+                .add_2bytes(msg.id.to_be_bytes())
+                .add_2bytes(msg.seq.to_be_bytes())
+                .add_4bytes(msg.originate_timestamp.to_be_bytes())
+                .add_4bytes(msg.receive_timestamp.to_be_bytes())
+                .add_4bytes(msg.transmit_timestamp.to_be_bytes()),
+            TimestampReply(msg) => checksum::Sum16BitWords::new()
+                .add_2bytes([TYPE_TIMESTAMP_REPLY, 0])
+                .add_2bytes(msg.id.to_be_bytes())
+                .add_2bytes(msg.seq.to_be_bytes())
+                .add_4bytes(msg.originate_timestamp.to_be_bytes())
+                .add_4bytes(msg.receive_timestamp.to_be_bytes())
+                .add_4bytes(msg.transmit_timestamp.to_be_bytes()),
+        }
+        .add_slice(payload)
+        .ones_complement()
+        .to_be()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{icmpv4::*, Icmpv4Type::*, *};
+    use alloc::format;
+    use proptest::prelude::*;
+
+    #[test]
+    fn header_len() {
+        let dummy_ts = TimestampMessage {
+            id: 0,
+            seq: 0,
+            originate_timestamp: 0,
+            receive_timestamp: 0,
+            transmit_timestamp: 0,
+        };
+        let dummy_echo = IcmpEchoHeader { id: 0, seq: 0 };
+        let dummy_redirect = RedirectHeader {
+            code: RedirectCode::RedirectForNetwork,
+            gateway_internet_address: [0; 4],
+        };
+        let tests = [
+            (
+                8,
+                Unknown {
+                    type_u8: 0,
+                    code_u8: 0,
+                    bytes5to8: [0; 4],
+                },
+            ),
+            (8, EchoReply(dummy_echo)),
+            (8, DestinationUnreachable(DestUnreachableHeader::Network)),
+            (8, Redirect(dummy_redirect)),
+            (8, EchoRequest(dummy_echo)),
+            (8, TimeExceeded(TimeExceededCode::TtlExceededInTransit)),
+            (8, ParameterProblem(ParameterProblemHeader::BadLength)),
+            (20, TimestampRequest(dummy_ts.clone())),
+            (20, TimestampReply(dummy_ts)),
+        ];
+        for t in tests {
+            assert_eq!(t.0, t.1.header_len());
+        }
+    }
+
+    #[test]
+    fn fixed_payload_size() {
+        use Icmpv4Type::*;
+
+        let dummy_ts = TimestampMessage {
+            id: 0,
+            seq: 0,
+            originate_timestamp: 0,
+            receive_timestamp: 0,
+            transmit_timestamp: 0,
+        };
+        let dummy_echo = IcmpEchoHeader { id: 0, seq: 0 };
+        let dummy_redirect = RedirectHeader {
+            code: RedirectCode::RedirectForNetwork,
+            gateway_internet_address: [0; 4],
+        };
+        let tests = [
+            (
+                None,
+                Unknown {
+                    type_u8: 0,
+                    code_u8: 0,
+                    bytes5to8: [0; 4],
+                },
+            ),
+            (None, EchoReply(dummy_echo)),
+            (None, DestinationUnreachable(DestUnreachableHeader::Network)),
+            (None, Redirect(dummy_redirect)),
+            (None, EchoRequest(dummy_echo)),
+            (None, TimeExceeded(TimeExceededCode::TtlExceededInTransit)),
+            (None, ParameterProblem(ParameterProblemHeader::BadLength)),
+            (Some(0), TimestampRequest(dummy_ts.clone())),
+            (Some(0), TimestampReply(dummy_ts)),
+        ];
+        for t in tests {
+            assert_eq!(t.0, t.1.fixed_payload_size());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn calc_checksum(
+            dest_unreach_code_u8 in 0u8..=15,
+            next_hop_mtu in any::<u16>(),
+            redirect_code_u8 in 0u8..=3,
+            gateway_internet_address in any::<[u8;4]>(),
+            time_exceeded_code_u8 in 0u8..=1,
+            id in any::<u16>(),
+            seq in any::<u16>(),
+            originate_timestamp in any::<u32>(),
+            receive_timestamp in any::<u32>(),
+            transmit_timestamp in any::<u32>(),
+            param_problem_code_u8 in 0u8..=2,
+            pointer in any::<u8>(),
+            unknown_type_u8 in any::<u8>(),
+            unknown_code_u8 in any::<u8>(),
+            bytes5to8 in any::<[u8;4]>(),
+            payload in proptest::collection::vec(any::<u8>(), 0..1024)
+        ) {
+            let ts = TimestampMessage{
+                id,
+                seq,
+                originate_timestamp,
+                receive_timestamp,
+                transmit_timestamp,
+            };
+            let echo = IcmpEchoHeader{
+                id,
+                seq,
+            };
+            let redirect = RedirectHeader{
+                code: RedirectCode::from_u8(redirect_code_u8).unwrap(),
+                gateway_internet_address,
+            };
+            let dest_unreach = DestUnreachableHeader::from_values(dest_unreach_code_u8, next_hop_mtu).unwrap();
+            let param_prob = ParameterProblemHeader::from_values(param_problem_code_u8, pointer).unwrap();
+            let values = [
+                Unknown {
+                    type_u8: unknown_type_u8,
+                    code_u8: unknown_code_u8,
+                    bytes5to8: bytes5to8,
+                },
+                EchoReply(echo.clone()),
+                DestinationUnreachable(dest_unreach),
+                Redirect(redirect),
+                EchoRequest(echo),
+                TimeExceeded(TimeExceededCode::from_u8(time_exceeded_code_u8).unwrap()),
+                ParameterProblem(param_prob),
+                TimestampRequest(ts.clone()),
+                TimestampReply(ts),
+            ];
+
+            for t in values {
+                let bytes = Icmpv4Header{
+                    icmp_type: t.clone(),
+                    checksum: 0, // use zero so the checksum calculation from the bytes works
+                }.to_bytes();
+                let expected = crate::checksum::Sum16BitWords::new()
+                    .add_slice(bytes.as_ref())
+                    .add_slice(&payload)
+                    .ones_complement()
+                    .to_be();
+                assert_eq!(expected, t.calc_checksum(&payload));
+            }
+        }
+    }
+
+    #[test]
+    fn clone_eq() {
+        let dummy_ts = TimestampMessage {
+            id: 0,
+            seq: 0,
+            originate_timestamp: 0,
+            receive_timestamp: 0,
+            transmit_timestamp: 0,
+        };
+        let dummy_echo = IcmpEchoHeader { id: 0, seq: 0 };
+        let dummy_redirect = RedirectHeader {
+            code: RedirectCode::RedirectForNetwork,
+            gateway_internet_address: [0; 4],
+        };
+        let tests = [
+            Unknown {
+                type_u8: 0,
+                code_u8: 0,
+                bytes5to8: [0; 4],
+            },
+            EchoReply(dummy_echo),
+            DestinationUnreachable(DestUnreachableHeader::Network),
+            Redirect(dummy_redirect),
+            EchoRequest(dummy_echo),
+            TimeExceeded(TimeExceededCode::TtlExceededInTransit),
+            ParameterProblem(ParameterProblemHeader::BadLength),
+            TimestampRequest(dummy_ts.clone()),
+            TimestampReply(dummy_ts),
+        ];
+        for t in tests {
+            assert_eq!(t.clone(), t);
+        }
+    }
+
+    #[test]
+    fn debug() {
+        let dummy_ts = TimestampMessage {
+            id: 0,
+            seq: 0,
+            originate_timestamp: 0,
+            receive_timestamp: 0,
+            transmit_timestamp: 0,
+        };
+        let dummy_echo = IcmpEchoHeader { id: 0, seq: 0 };
+
+        assert_eq!(
+            format!(
+                "{:?}",
+                Unknown {
+                    type_u8: 0,
+                    code_u8: 0,
+                    bytes5to8: [0; 4]
+                }
+            ),
+            format!(
+                "Unknown {{ type_u8: {:?}, code_u8: {:?}, bytes5to8: {:?} }}",
+                0u8, 0u8, [0u8; 4]
+            )
+        );
+        assert_eq!(
+            format!("{:?}", EchoReply(dummy_echo)),
+            format!("EchoReply({:?})", dummy_echo)
+        );
+        assert_eq!(
+            format!(
+                "{:?}",
+                DestinationUnreachable(DestUnreachableHeader::Network)
+            ),
+            format!(
+                "DestinationUnreachable({:?})",
+                DestUnreachableHeader::Network
+            )
+        );
+        {
+            let dummy_redirect = RedirectHeader {
+                code: RedirectCode::RedirectForNetwork,
+                gateway_internet_address: [0; 4],
+            };
+            assert_eq!(
+                format!("{:?}", Redirect(dummy_redirect.clone())),
+                format!("Redirect({:?})", dummy_redirect)
+            );
+        }
+        assert_eq!(
+            format!("{:?}", EchoRequest(dummy_echo)),
+            format!("EchoRequest({:?})", dummy_echo)
+        );
+        assert_eq!(
+            format!("{:?}", TimeExceeded(TimeExceededCode::TtlExceededInTransit)),
+            format!("TimeExceeded({:?})", TimeExceededCode::TtlExceededInTransit)
+        );
+        assert_eq!(
+            format!("{:?}", ParameterProblem(ParameterProblemHeader::BadLength)),
+            format!("ParameterProblem({:?})", ParameterProblemHeader::BadLength)
+        );
+        assert_eq!(
+            format!("{:?}", TimestampRequest(dummy_ts.clone())),
+            format!("TimestampRequest({:?})", dummy_ts)
+        );
+        assert_eq!(
+            format!("{:?}", TimestampReply(dummy_ts.clone())),
+            format!("TimestampReply({:?})", dummy_ts)
+        );
+    }
+}
diff --git a/src/transport/icmpv6/dest_unreachable_code.rs b/src/transport/icmpv6/dest_unreachable_code.rs
new file mode 100644
index 0000000..4f72d1b
--- /dev/null
+++ b/src/transport/icmpv6/dest_unreachable_code.rs
@@ -0,0 +1,136 @@
+use super::*;
+
+/// "Destination Unreachable" ICMPv6 code containing a reason why a
+/// destination could not be reached.
+///
+/// # RFC 4443 Description:
+///
+/// A Destination Unreachable message SHOULD be generated by a router, or
+/// by the IPv6 layer in the originating node, in response to a packet
+/// 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)]
+pub enum DestUnreachableCode {
+    /// No route to destination
+    NoRoute = 0,
+    /// Communication with destination administratively prohibited
+    Prohibited = 1,
+    /// Beyond scope of source address
+    BeyondScope = 2,
+    /// Address unreachable
+    Address = 3,
+    /// Port unreachable
+    Port = 4,
+    /// Source address failed ingress/egress policy
+    SourceAddressFailedPolicy = 5,
+    /// Reject route to destination
+    RejectRoute = 6,
+}
+
+impl DestUnreachableCode {
+    /// Converts the u8 code value from an ICMPv6 "destination unreachable"
+    /// packet to an `icmpv6::DestUnreachableCode` enum.
+    ///
+    /// # Example Usage:
+    ///
+    /// ```
+    /// use etherparse::{icmpv6, icmpv6::DestUnreachableCode};
+    /// let icmp_packet: [u8;8] = [
+    ///     icmpv6::TYPE_DST_UNREACH, icmpv6::CODE_DST_UNREACH_PORT, 0, 0,
+    ///     0, 0, 0, 0,
+    /// ];
+    ///
+    /// if icmpv6::TYPE_DST_UNREACH == icmp_packet[0] {
+    ///     let dst = icmpv6::DestUnreachableCode::from_u8(
+    ///         icmp_packet[1]
+    ///     );
+    ///     assert_eq!(dst, Some(icmpv6::DestUnreachableCode::Port));
+    /// }
+    /// ```
+    pub fn from_u8(code_u8: u8) -> Option<DestUnreachableCode> {
+        use DestUnreachableCode::*;
+        match code_u8 {
+            CODE_DST_UNREACH_NO_ROUTE => Some(NoRoute),
+            CODE_DST_UNREACH_PROHIBITED => Some(Prohibited),
+            CODE_DST_UNREACH_BEYOND_SCOPE => Some(BeyondScope),
+            CODE_DST_UNREACH_ADDR => Some(Address),
+            CODE_DST_UNREACH_PORT => Some(Port),
+            CODE_DST_UNREACH_SOURCE_ADDRESS_FAILED_POLICY => Some(SourceAddressFailedPolicy),
+            CODE_DST_UNREACH_REJECT_ROUTE_TO_DEST => Some(RejectRoute),
+            _ => None,
+        }
+    }
+
+    /// Returns the code value of the destination unreachable packet.
+    ///
+    /// This is the second byte of an ICMPv6 packet.
+    #[inline]
+    pub fn code_u8(&self) -> u8 {
+        *self as u8
+    }
+}
+
+#[cfg(test)]
+pub(crate) mod dest_unreachable_code_test_consts {
+    use super::{DestUnreachableCode::*, *};
+
+    pub const VALID_VALUES: [(DestUnreachableCode, u8); 7] = [
+        (NoRoute, CODE_DST_UNREACH_NO_ROUTE),
+        (Prohibited, CODE_DST_UNREACH_PROHIBITED),
+        (BeyondScope, CODE_DST_UNREACH_BEYOND_SCOPE),
+        (Address, CODE_DST_UNREACH_ADDR),
+        (Port, CODE_DST_UNREACH_PORT),
+        (
+            SourceAddressFailedPolicy,
+            CODE_DST_UNREACH_SOURCE_ADDRESS_FAILED_POLICY,
+        ),
+        (RejectRoute, CODE_DST_UNREACH_REJECT_ROUTE_TO_DEST),
+    ];
+}
+
+#[cfg(test)]
+mod test {
+    use super::{dest_unreachable_code_test_consts::*, DestUnreachableCode::*, *};
+    use alloc::format;
+
+    #[test]
+    fn from_u8() {
+        for (code, code_u8) in VALID_VALUES {
+            assert_eq!(code, DestUnreachableCode::from_u8(code_u8).unwrap());
+        }
+        for code_u8 in 7u8..=0xff {
+            assert!(DestUnreachableCode::from_u8(code_u8).is_none());
+        }
+    }
+
+    #[test]
+    fn code_u8() {
+        for (code, code_u8) in VALID_VALUES {
+            assert_eq!(code.code_u8(), code_u8);
+        }
+    }
+
+    #[test]
+    fn clone_eq() {
+        for (code, _) in VALID_VALUES {
+            assert_eq!(code.clone(), code);
+        }
+    }
+
+    #[test]
+    fn debug() {
+        let tests = [
+            (NoRoute, "NoRoute"),
+            (Prohibited, "Prohibited"),
+            (BeyondScope, "BeyondScope"),
+            (Address, "Address"),
+            (Port, "Port"),
+            (SourceAddressFailedPolicy, "SourceAddressFailedPolicy"),
+            (RejectRoute, "RejectRoute"),
+        ];
+        for test in tests {
+            assert_eq!(format!("{:?}", test.0), test.1);
+        }
+    }
+}
diff --git a/src/transport/icmpv6/mod.rs b/src/transport/icmpv6/mod.rs
new file mode 100644
index 0000000..aa370fe
--- /dev/null
+++ b/src/transport/icmpv6/mod.rs
@@ -0,0 +1,206 @@
+mod dest_unreachable_code;
+pub use dest_unreachable_code::*;
+
+mod parameter_problem_code;
+pub use parameter_problem_code::*;
+
+mod parameter_problem_header;
+pub use parameter_problem_header::*;
+
+mod time_exceeded_code;
+pub use time_exceeded_code::*;
+
+/// The maximum number of bytes/octets the ICMPv6 part of a packet can contain.
+///
+/// The value is determined by the maximum value of the "Upper-Layer Packet Length"
+/// field. This field is not directly part of the packet but used during the checksum
+/// calculation in the pseudo header.
+///
+/// The "Upper-Layer Packet Length" is represented as an `u32` and defined as
+/// "...the Payload Length from the IPv6 header, minus the length of any
+/// extension headers present between the IPv6 header and the upper-layer
+/// header" (according to RFC 2460 Section 8.1). In other words, the length of the
+/// ICMPv6 part of the packet.
+///
+/// Therefor the maximum size of an ICMPv6 packet is `u32::MAX`.
+pub const MAX_ICMPV6_BYTE_LEN: usize = u32::MAX as usize;
+
+/// ICMPv6 type value indicating a "Destination Unreachable" message.
+pub const TYPE_DST_UNREACH: u8 = 1;
+
+/// ICMPv6 type value indicating a "Packet Too Big" message.
+pub const TYPE_PACKET_TOO_BIG: u8 = 2;
+
+/// ICMPv6 type value indicating a "Time Exceeded" message.
+pub const TYPE_TIME_EXCEEDED: u8 = 3;
+
+/// ICMPv6 type value indicating a "Parameter Problem" message.
+pub const TYPE_PARAMETER_PROBLEM: u8 = 4;
+
+/// ICMPv6 type value indicating an "Echo Request" message.
+pub const TYPE_ECHO_REQUEST: u8 = 128;
+
+/// ICMPv6 type value indicating an "Echo Reply" message.
+pub const TYPE_ECHO_REPLY: u8 = 129;
+
+/// ICMPv6 type value indicating a "Multicast Listener Query" message.
+pub const TYPE_MULTICAST_LISTENER_QUERY: u8 = 130;
+
+/// ICMPv6 type value indicating a "Multicast Listener Report" message.
+pub const TYPE_MULTICAST_LISTENER_REPORT: u8 = 131;
+
+/// ICMPv6 type value indicating a "Multicast Listener Done" message.
+pub const TYPE_MULTICAST_LISTENER_REDUCTION: u8 = 132;
+
+/// ICMPv6 type value indicating a "Router Solicitation" message.
+pub const TYPE_ROUTER_SOLICITATION: u8 = 133;
+
+/// ICMPv6 type value indicating a "Router Advertisement" message.
+pub const TYPE_ROUTER_ADVERTISEMENT: u8 = 134;
+
+/// ICMPv6 type value indicating a "Neighbor Solicitation" message.
+pub const TYPE_NEIGHBOR_SOLICITATION: u8 = 135;
+
+/// ICMPv6 type value indicating a "Neighbor Advertisement" message.
+pub const TYPE_NEIGHBOR_ADVERTISEMENT: u8 = 136;
+
+/// ICMPv6 type value indicating a "Redirect Message" message.
+pub const TYPE_REDIRECT_MESSAGE: u8 = 137;
+
+/// ICMPv6 type value indicating a "Router Renumbering" message.
+pub const TYPE_ROUTER_RENUMBERING: u8 = 138;
+
+/// ICMPv6 type value indicating a "Inverse Neighbor Discovery Solicitation" message.
+pub const TYPE_INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION: u8 = 141;
+
+/// ICMPv6 type value indicating a "Inverse Neighbor Discovery Advertisement" message.
+pub const TYPE_INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT: u8 = 142;
+
+/// ICMPv6 type value indicating a "Extended Echo Request" message.
+pub const TYPE_EXT_ECHO_REQUEST: u8 = 160;
+
+/// ICMPv6 type value indicating a "Extended Echo Reply" message.
+pub const TYPE_EXT_ECHO_REPLY: u8 = 161;
+
+/// ICMPv6 destination unreachable code for "no route to destination".
+pub const CODE_DST_UNREACH_NO_ROUTE: u8 = 0;
+
+/// ICMPv6 destination unreachable code for "communication with
+/// destination administratively prohibited".
+pub const CODE_DST_UNREACH_PROHIBITED: u8 = 1;
+
+/// ICMPv6 destination unreachable code for "beyond scope of source address".
+pub const CODE_DST_UNREACH_BEYOND_SCOPE: u8 = 2;
+
+/// ICMPv6 destination unreachable code for "address unreachable".
+pub const CODE_DST_UNREACH_ADDR: u8 = 3;
+
+/// ICMPv6 destination unreachable code for "port unreachable".
+pub const CODE_DST_UNREACH_PORT: u8 = 4;
+
+/// ICMPv6 destination unreachable code for "source address failed ingress/egress policy".
+pub const CODE_DST_UNREACH_SOURCE_ADDRESS_FAILED_POLICY: u8 = 5;
+
+/// ICMPv6 destination unreachable code for "reject route to destination".
+pub const CODE_DST_UNREACH_REJECT_ROUTE_TO_DEST: u8 = 6;
+
+/// ICMPv6 time exceeded code for "hop limit exceeded in transit"
+pub const CODE_TIME_EXCEEDED_HOP_LIMIT_EXCEEDED: u8 = 0;
+
+/// ICMPv6 time exceeded code for "fragment reassembly time exceeded"
+pub const CODE_TIME_EXCEEDED_FRAGMENT_REASSEMBLY_TIME_EXCEEDED: u8 = 1;
+
+/// ICMPv6 parameter problem code for "erroneous header field encountered" (from [RFC 4443](https://tools.ietf.org/html/rfc4443)).
+pub const CODE_PARAM_PROBLEM_ERR_HEADER_FIELD: u8 = 0;
+
+/// ICMPv6 parameter problem code for "unrecognized Next Header type encountered" (from [RFC 4443](https://tools.ietf.org/html/rfc4443)).
+pub const CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER: u8 = 1;
+
+/// ICMPv6 parameter problem code for "unrecognized IPv6 option encountered" (from [RFC 4443](https://tools.ietf.org/html/rfc4443)).
+pub const CODE_PARAM_PROBLEM_UNRECOG_IPV6_OPTION: u8 = 2;
+
+/// ICMPv6 parameter problem code for "IPv6 First Fragment has incomplete IPv6 Header Chain" (from [RFC 7112](https://tools.ietf.org/html/rfc7112)).
+pub const CODE_PARAM_PROBLEM_IPV6_FIRST_FRAG_INCOMP_HEADER_CHAIN: u8 = 3;
+
+/// ICMPv6 parameter problem code for "SR Upper-layer Header Error" (from [RFC 8754](https://tools.ietf.org/html/rfc8754)).
+pub const CODE_PARAM_PROBLEM_SR_UPPER_LAYER_HEADER_ERROR: u8 = 4;
+
+/// ICMPv6 parameter problem code for "Unrecognized Next Header type encountered by intermediate node" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)).
+pub const CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER_BY_INTERMEDIATE_NODE: u8 = 5;
+
+/// ICMPv6 parameter problem code for "Extension header too big" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)).
+pub const CODE_PARAM_PROBLEM_EXT_HEADER_TOO_BIG: u8 = 6;
+
+/// ICMPv6 parameter problem code for "Extension header chain too long" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)).
+pub const CODE_PARAM_PROBLEM_EXT_HEADER_CHAIN_TOO_LONG: u8 = 7;
+
+/// ICMPv6 parameter problem code for "Too many extension headers" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)).
+pub const CODE_PARAM_PROBLEM_TOO_MANY_EXT_HEADERS: u8 = 8;
+
+/// ICMPv6 parameter problem code for "Too many options in extension header" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)).
+pub const CODE_PARAM_PROBLEM_TOO_MANY_OPTIONS_EXT_HEADER: u8 = 9;
+
+/// ICMPv6 parameter problem code for "Option too big" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)).
+pub const CODE_PARAM_PROBLEM_OPTION_TOO_BIG: u8 = 10;
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn constants() {
+        // type values according to
+        // https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-codes-16
+        assert_eq!(1, TYPE_DST_UNREACH);
+        assert_eq!(2, TYPE_PACKET_TOO_BIG);
+        assert_eq!(3, TYPE_TIME_EXCEEDED);
+        assert_eq!(4, TYPE_PARAMETER_PROBLEM);
+        assert_eq!(128, TYPE_ECHO_REQUEST);
+        assert_eq!(129, TYPE_ECHO_REPLY);
+        assert_eq!(130, TYPE_MULTICAST_LISTENER_QUERY);
+        assert_eq!(131, TYPE_MULTICAST_LISTENER_REPORT);
+        assert_eq!(132, TYPE_MULTICAST_LISTENER_REDUCTION);
+        assert_eq!(133, TYPE_ROUTER_SOLICITATION);
+        assert_eq!(134, TYPE_ROUTER_ADVERTISEMENT);
+        assert_eq!(135, TYPE_NEIGHBOR_SOLICITATION);
+        assert_eq!(136, TYPE_NEIGHBOR_ADVERTISEMENT);
+        assert_eq!(137, TYPE_REDIRECT_MESSAGE);
+        assert_eq!(138, TYPE_ROUTER_RENUMBERING);
+        assert_eq!(141, TYPE_INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION);
+        assert_eq!(142, TYPE_INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT);
+        assert_eq!(160, TYPE_EXT_ECHO_REQUEST);
+        assert_eq!(161, TYPE_EXT_ECHO_REPLY);
+
+        // destination unreachable code values according to
+        // https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-codes-2
+        assert_eq!(0, CODE_DST_UNREACH_NO_ROUTE);
+        assert_eq!(1, CODE_DST_UNREACH_PROHIBITED);
+        assert_eq!(2, CODE_DST_UNREACH_BEYOND_SCOPE);
+        assert_eq!(3, CODE_DST_UNREACH_ADDR);
+        assert_eq!(4, CODE_DST_UNREACH_PORT);
+        assert_eq!(5, CODE_DST_UNREACH_SOURCE_ADDRESS_FAILED_POLICY);
+        assert_eq!(6, CODE_DST_UNREACH_REJECT_ROUTE_TO_DEST);
+
+        // time exceeded code values according to
+        // https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-codes-4
+        assert_eq!(0, CODE_TIME_EXCEEDED_HOP_LIMIT_EXCEEDED);
+        assert_eq!(1, CODE_TIME_EXCEEDED_FRAGMENT_REASSEMBLY_TIME_EXCEEDED);
+
+        // parameter problem codes according to
+        // https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-codes-5
+        assert_eq!(0, CODE_PARAM_PROBLEM_ERR_HEADER_FIELD);
+        assert_eq!(1, CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER);
+        assert_eq!(2, CODE_PARAM_PROBLEM_UNRECOG_IPV6_OPTION);
+        assert_eq!(3, CODE_PARAM_PROBLEM_IPV6_FIRST_FRAG_INCOMP_HEADER_CHAIN);
+        assert_eq!(4, CODE_PARAM_PROBLEM_SR_UPPER_LAYER_HEADER_ERROR);
+        assert_eq!(
+            5,
+            CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER_BY_INTERMEDIATE_NODE
+        );
+        assert_eq!(6, CODE_PARAM_PROBLEM_EXT_HEADER_TOO_BIG);
+        assert_eq!(7, CODE_PARAM_PROBLEM_EXT_HEADER_CHAIN_TOO_LONG);
+        assert_eq!(8, CODE_PARAM_PROBLEM_TOO_MANY_EXT_HEADERS);
+        assert_eq!(9, CODE_PARAM_PROBLEM_TOO_MANY_OPTIONS_EXT_HEADER);
+        assert_eq!(10, CODE_PARAM_PROBLEM_OPTION_TOO_BIG);
+    }
+}
diff --git a/src/transport/icmpv6/parameter_problem_code.rs b/src/transport/icmpv6/parameter_problem_code.rs
new file mode 100644
index 0000000..7616401
--- /dev/null
+++ b/src/transport/icmpv6/parameter_problem_code.rs
@@ -0,0 +1,168 @@
+use super::*;
+
+/// Code values for ICMPv6 parameter problem messages.
+///
+/// Source: <https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-codes-5>
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum ParameterProblemCode {
+    /// Erroneous header field encountered (from [RFC 4443](https://tools.ietf.org/html/rfc4443))
+    ErroneousHeaderField = 0,
+    /// Unrecognized Next Header type encountered (from [RFC 4443](https://tools.ietf.org/html/rfc4443))
+    UnrecognizedNextHeader = 1,
+    /// Unrecognized IPv6 option encountered (from [RFC 4443](https://tools.ietf.org/html/rfc4443))
+    UnrecognizedIpv6Option = 2,
+    /// IPv6 First Fragment has incomplete IPv6 Header Chain (from [RFC 7112](https://tools.ietf.org/html/rfc7112))
+    Ipv6FirstFragmentIncompleteHeaderChain = 3,
+    /// SR Upper-layer Header Error (from [RFC 8754](https://tools.ietf.org/html/rfc8754)).
+    SrUpperLayerHeaderError = 4,
+    /// Unrecognized Next Header type encountered by intermediate node (from [RFC 8883](https://tools.ietf.org/html/rfc8883))
+    UnrecognizedNextHeaderByIntermediateNode = 5,
+    /// Extension header too big (from [RFC 8883](https://tools.ietf.org/html/rfc8883))
+    ExtensionHeaderTooBig = 6,
+    /// Extension header chain too long (from [RFC 8883](https://tools.ietf.org/html/rfc8883))
+    ExtensionHeaderChainTooLong = 7,
+    /// Too many extension headers (from [RFC 8883](https://tools.ietf.org/html/rfc8883))
+    TooManyExtensionHeaders = 8,
+    /// Too many options in extension header (from [RFC 8883](https://tools.ietf.org/html/rfc8883))
+    TooManyOptionsInExtensionHeader = 9,
+    /// Option too big (from [RFC 8883](https://tools.ietf.org/html/rfc8883))
+    OptionTooBig = 10,
+}
+
+impl ParameterProblemCode {
+    /// Tries to convert a code [`u8`] value to a [`ParameterProblemCode`] value.
+    ///
+    /// Returns [`None`] in case the code value is not known as a parameter problem code.
+    pub fn from_u8(code_u8: u8) -> Option<ParameterProblemCode> {
+        use ParameterProblemCode::*;
+        match code_u8 {
+            CODE_PARAM_PROBLEM_ERR_HEADER_FIELD => Some(ErroneousHeaderField),
+            CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER => Some(UnrecognizedNextHeader),
+            CODE_PARAM_PROBLEM_UNRECOG_IPV6_OPTION => Some(UnrecognizedIpv6Option),
+            CODE_PARAM_PROBLEM_IPV6_FIRST_FRAG_INCOMP_HEADER_CHAIN => {
+                Some(Ipv6FirstFragmentIncompleteHeaderChain)
+            }
+            CODE_PARAM_PROBLEM_SR_UPPER_LAYER_HEADER_ERROR => Some(SrUpperLayerHeaderError),
+            CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER_BY_INTERMEDIATE_NODE => {
+                Some(UnrecognizedNextHeaderByIntermediateNode)
+            }
+            CODE_PARAM_PROBLEM_EXT_HEADER_TOO_BIG => Some(ExtensionHeaderTooBig),
+            CODE_PARAM_PROBLEM_EXT_HEADER_CHAIN_TOO_LONG => Some(ExtensionHeaderChainTooLong),
+            CODE_PARAM_PROBLEM_TOO_MANY_EXT_HEADERS => Some(TooManyExtensionHeaders),
+            CODE_PARAM_PROBLEM_TOO_MANY_OPTIONS_EXT_HEADER => Some(TooManyOptionsInExtensionHeader),
+            CODE_PARAM_PROBLEM_OPTION_TOO_BIG => Some(OptionTooBig),
+            _ => None,
+        }
+    }
+
+    /// Returns the [`u8`] value of the code.
+    #[inline]
+    pub fn code_u8(&self) -> u8 {
+        *self as u8
+    }
+}
+
+#[cfg(test)]
+pub(crate) mod parameter_problem_code_test_consts {
+    use super::*;
+    use ParameterProblemCode::*;
+
+    pub const VALID_VALUES: [(ParameterProblemCode, u8); 11] = [
+        (ErroneousHeaderField, CODE_PARAM_PROBLEM_ERR_HEADER_FIELD),
+        (
+            UnrecognizedNextHeader,
+            CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER,
+        ),
+        (
+            UnrecognizedIpv6Option,
+            CODE_PARAM_PROBLEM_UNRECOG_IPV6_OPTION,
+        ),
+        (
+            Ipv6FirstFragmentIncompleteHeaderChain,
+            CODE_PARAM_PROBLEM_IPV6_FIRST_FRAG_INCOMP_HEADER_CHAIN,
+        ),
+        (
+            SrUpperLayerHeaderError,
+            CODE_PARAM_PROBLEM_SR_UPPER_LAYER_HEADER_ERROR,
+        ),
+        (
+            UnrecognizedNextHeaderByIntermediateNode,
+            CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER_BY_INTERMEDIATE_NODE,
+        ),
+        (ExtensionHeaderTooBig, CODE_PARAM_PROBLEM_EXT_HEADER_TOO_BIG),
+        (
+            ExtensionHeaderChainTooLong,
+            CODE_PARAM_PROBLEM_EXT_HEADER_CHAIN_TOO_LONG,
+        ),
+        (
+            TooManyExtensionHeaders,
+            CODE_PARAM_PROBLEM_TOO_MANY_EXT_HEADERS,
+        ),
+        (
+            TooManyOptionsInExtensionHeader,
+            CODE_PARAM_PROBLEM_TOO_MANY_OPTIONS_EXT_HEADER,
+        ),
+        (OptionTooBig, CODE_PARAM_PROBLEM_OPTION_TOO_BIG),
+    ];
+}
+
+#[cfg(test)]
+mod test {
+    use super::{parameter_problem_code_test_consts::*, ParameterProblemCode::*, *};
+    use alloc::format;
+
+    #[test]
+    fn from_u8() {
+        for t in VALID_VALUES {
+            assert_eq!(Some(t.0), ParameterProblemCode::from_u8(t.1));
+        }
+
+        for code_u8 in 11..=u8::MAX {
+            assert_eq!(None, ParameterProblemCode::from_u8(code_u8));
+        }
+    }
+
+    #[test]
+    fn code_u8() {
+        for t in VALID_VALUES {
+            assert_eq!(t.0.code_u8(), t.1);
+        }
+    }
+    #[test]
+    fn clone_eq() {
+        for (value, _) in VALID_VALUES {
+            assert_eq!(value.clone(), value);
+        }
+    }
+
+    #[test]
+    fn debug() {
+        let tests = [
+            (ErroneousHeaderField, "ErroneousHeaderField"),
+            (UnrecognizedNextHeader, "UnrecognizedNextHeader"),
+            (UnrecognizedIpv6Option, "UnrecognizedIpv6Option"),
+            (UnrecognizedNextHeader, "UnrecognizedNextHeader"),
+            (UnrecognizedIpv6Option, "UnrecognizedIpv6Option"),
+            (
+                Ipv6FirstFragmentIncompleteHeaderChain,
+                "Ipv6FirstFragmentIncompleteHeaderChain",
+            ),
+            (SrUpperLayerHeaderError, "SrUpperLayerHeaderError"),
+            (
+                UnrecognizedNextHeaderByIntermediateNode,
+                "UnrecognizedNextHeaderByIntermediateNode",
+            ),
+            (ExtensionHeaderTooBig, "ExtensionHeaderTooBig"),
+            (ExtensionHeaderChainTooLong, "ExtensionHeaderChainTooLong"),
+            (TooManyExtensionHeaders, "TooManyExtensionHeaders"),
+            (
+                TooManyOptionsInExtensionHeader,
+                "TooManyOptionsInExtensionHeader",
+            ),
+            (OptionTooBig, "OptionTooBig"),
+        ];
+        for test in tests {
+            assert_eq!(format!("{:?}", test.0), test.1);
+        }
+    }
+}
diff --git a/src/transport/icmpv6/parameter_problem_header.rs b/src/transport/icmpv6/parameter_problem_header.rs
new file mode 100644
index 0000000..c189681
--- /dev/null
+++ b/src/transport/icmpv6/parameter_problem_header.rs
@@ -0,0 +1,47 @@
+use super::*;
+
+/// ICMPv6 parameter problem header.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct ParameterProblemHeader {
+    /// The code can offer additional informations about what kind of parameter
+    /// problem caused the error.
+    pub code: ParameterProblemCode,
+    /// Identifies the octet offset within the
+    /// invoking packet where the error was detected.
+    ///
+    /// The pointer will point beyond the end of the ICMPv6
+    /// packet if the field in error is beyond what can fit
+    /// in the maximum size of an ICMPv6 error message.
+    pub pointer: u32,
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use alloc::format;
+
+    #[test]
+    fn clone_eq() {
+        let value = ParameterProblemHeader {
+            code: ParameterProblemCode::ErroneousHeaderField,
+            pointer: 0,
+        };
+        assert_eq!(value.clone(), value);
+    }
+
+    #[test]
+    fn debug() {
+        let value = ParameterProblemHeader {
+            code: ParameterProblemCode::ErroneousHeaderField,
+            pointer: 0,
+        };
+
+        assert_eq!(
+            format!("{:?}", value),
+            format!(
+                "ParameterProblemHeader {{ code: {:?}, pointer: {:?} }}",
+                value.code, value.pointer
+            )
+        );
+    }
+}
diff --git a/src/transport/icmpv6/time_exceeded_code.rs b/src/transport/icmpv6/time_exceeded_code.rs
new file mode 100644
index 0000000..9f06030
--- /dev/null
+++ b/src/transport/icmpv6/time_exceeded_code.rs
@@ -0,0 +1,90 @@
+use super::*;
+
+/// Code values for ICMPv6 time exceeded message.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum TimeExceededCode {
+    /// "hop limit exceeded in transit"
+    HopLimitExceeded = 0,
+    /// "fragment reassembly time exceeded"
+    FragmentReassemblyTimeExceeded = 1,
+}
+
+impl TimeExceededCode {
+    /// Tries to convert a code [`u8`] value to a [`TimeExceededCode`] value.
+    ///
+    /// Returns [`None`] in case the code value is not known as a time exceeded code.
+    #[inline]
+    pub fn from_u8(code_u8: u8) -> Option<TimeExceededCode> {
+        use TimeExceededCode::*;
+        match code_u8 {
+            CODE_TIME_EXCEEDED_HOP_LIMIT_EXCEEDED => Some(HopLimitExceeded),
+            CODE_TIME_EXCEEDED_FRAGMENT_REASSEMBLY_TIME_EXCEEDED => {
+                Some(FragmentReassemblyTimeExceeded)
+            }
+            _ => None,
+        }
+    }
+
+    /// Returns the [`u8`] value of the code.
+    #[inline]
+    pub fn code_u8(&self) -> u8 {
+        *self as u8
+    }
+}
+
+#[cfg(test)]
+pub(crate) mod time_exceeded_code_test_consts {
+    use super::{TimeExceededCode::*, *};
+
+    pub const VALID_VALUES: [(TimeExceededCode, u8); 2] = [
+        (HopLimitExceeded, CODE_TIME_EXCEEDED_HOP_LIMIT_EXCEEDED),
+        (
+            FragmentReassemblyTimeExceeded,
+            CODE_TIME_EXCEEDED_FRAGMENT_REASSEMBLY_TIME_EXCEEDED,
+        ),
+    ];
+}
+
+#[cfg(test)]
+mod test {
+    use super::{time_exceeded_code_test_consts::*, TimeExceededCode::*, *};
+    use alloc::format;
+
+    #[test]
+    fn from_u8() {
+        for (code, code_u8) in VALID_VALUES {
+            assert_eq!(Some(code), TimeExceededCode::from_u8(code_u8));
+        }
+        for code_u8 in 2..=u8::MAX {
+            assert_eq!(None, TimeExceededCode::from_u8(code_u8));
+        }
+    }
+
+    #[test]
+    fn from_enum() {
+        for (code, code_u8) in VALID_VALUES {
+            assert_eq!(code.code_u8(), code_u8);
+        }
+    }
+
+    #[test]
+    fn clone_eq() {
+        for (code, _) in VALID_VALUES {
+            assert_eq!(code.clone(), code);
+        }
+    }
+
+    #[test]
+    fn debug() {
+        let tests = [
+            (HopLimitExceeded, "HopLimitExceeded"),
+            (
+                FragmentReassemblyTimeExceeded,
+                "FragmentReassemblyTimeExceeded",
+            ),
+        ];
+        for test in tests {
+            assert_eq!(format!("{:?}", test.0), test.1);
+        }
+    }
+}
diff --git a/src/transport/icmpv6_header.rs b/src/transport/icmpv6_header.rs
new file mode 100644
index 0000000..7eaef40
--- /dev/null
+++ b/src/transport/icmpv6_header.rs
@@ -0,0 +1,587 @@
+use crate::{err::ValueTooBigError, *};
+use arrayvec::ArrayVec;
+
+/// The statically sized data at the start of an ICMPv6 packet (at least the first 8 bytes of an ICMPv6 packet).
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct Icmpv6Header {
+    /// Type & type specific values & code.
+    pub icmp_type: Icmpv6Type,
+    /// Checksum in the ICMPv6 header.
+    pub checksum: u16,
+}
+
+impl Icmpv6Header {
+    /// Minimum number of bytes an ICMP header needs to have.
+    ///
+    /// Note that minimum size can be larger depending on
+    /// the type and code.
+    pub const MIN_LEN: usize = 8;
+
+    /// Deprecated, use [`Icmpv6Header::MIN_LEN`] instead.
+    #[deprecated(since = "0.14.0", note = "Please use Icmpv6Header::MIN_LEN instead")]
+    pub const MIN_SERIALIZED_SIZE: usize = Icmpv6Header::MIN_LEN;
+
+    /// Maximum number of bytes/octets an Icmpv6Header takes up
+    /// in serialized form.
+    ///
+    /// Currently this number is determined by the biggest
+    /// planned ICMPv6 header type, which is currently the
+    /// "Neighbor Discovery Protocol" "Redirect" message.
+    pub const MAX_LEN: usize = 8 + 16 + 16;
+
+    /// Deprecated, use [`Icmpv6Header::MAX_LEN`] instead.
+    #[deprecated(since = "0.14.0", note = "Please use Icmpv6Header::MAX_LEN instead")]
+    pub const MAX_SERIALIZED_SIZE: usize = Icmpv6Header::MAX_LEN;
+
+    /// Setups a new header with the checksum being set to 0.
+    #[inline]
+    pub fn new(icmp_type: Icmpv6Type) -> Icmpv6Header {
+        Icmpv6Header {
+            icmp_type,
+            checksum: 0, // will be filled in later
+        }
+    }
+
+    /// Creates a [`Icmpv6Header`] with a checksum calculated based
+    /// on the given payload & ip addresses from the IPv6 header.
+    pub fn with_checksum(
+        icmp_type: Icmpv6Type,
+        source_ip: [u8; 16],
+        destination_ip: [u8; 16],
+        payload: &[u8],
+    ) -> Result<Icmpv6Header, ValueTooBigError<usize>> {
+        let checksum = icmp_type.calc_checksum(source_ip, destination_ip, payload)?;
+        Ok(Icmpv6Header {
+            icmp_type,
+            checksum,
+        })
+    }
+
+    /// Reads an icmp6 header from a slice directly and returns a tuple
+    /// containing the resulting header & unused part of the slice.
+    #[inline]
+    pub fn from_slice(slice: &[u8]) -> Result<(Icmpv6Header, &[u8]), err::LenError> {
+        let header = Icmpv6Slice::from_slice(slice)?.header();
+        let len = header.header_len();
+        Ok((header, &slice[len..]))
+    }
+
+    /// Read a ICMPv6 header from the given reader
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read<T: std::io::Read + Sized>(reader: &mut T) -> Result<Icmpv6Header, std::io::Error> {
+        // read the initial 8 bytes
+        let mut start = [0u8; 8];
+        reader.read_exact(&mut start)?;
+        Ok(Icmpv6Slice { slice: &start }.header())
+    }
+
+    /// Write the ICMPv6 header to the given writer.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
+        writer.write_all(&self.to_bytes())
+    }
+
+    /// Serialized length of the header in bytes/octets.
+    ///
+    /// Note that this size is not the size of the entire
+    /// ICMPv6 packet but only the header.
+    #[inline]
+    pub fn header_len(&self) -> usize {
+        self.icmp_type.header_len()
+    }
+
+    /// If the ICMP type has a fixed size returns the number of
+    /// bytes that should be present after the header of this type.
+    #[inline]
+    pub fn fixed_payload_size(&self) -> Option<usize> {
+        self.icmp_type.fixed_payload_size()
+    }
+
+    /// Updates the checksum of the header.
+    pub fn update_checksum(
+        &mut self,
+        source_ip: [u8; 16],
+        destination_ip: [u8; 16],
+        payload: &[u8],
+    ) -> Result<(), ValueTooBigError<usize>> {
+        self.checksum = self
+            .icmp_type
+            .calc_checksum(source_ip, destination_ip, payload)?;
+        Ok(())
+    }
+
+    /// Returns the header on the wire bytes.
+    #[inline]
+    pub fn to_bytes(&self) -> ArrayVec<u8, { Icmpv6Header::MAX_LEN }> {
+        let checksum_be = self.checksum.to_be_bytes();
+
+        let return_trivial =
+            |type_u8: u8, code_u8: u8| -> ArrayVec<u8, { Icmpv6Header::MAX_LEN }> {
+                #[rustfmt::skip]
+            let mut re = ArrayVec::from([
+                type_u8, code_u8, checksum_be[0], checksum_be[1],
+                0, 0, 0, 0,
+
+                0, 0, 0, 0,
+                0, 0, 0, 0,
+                0, 0, 0, 0,
+                0, 0, 0, 0,
+
+                0, 0, 0, 0,
+                0, 0, 0, 0,
+                0, 0, 0, 0,
+                0, 0, 0, 0,
+            ]);
+                // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20.
+                unsafe {
+                    re.set_len(8);
+                }
+                re
+            };
+
+        let return_4u8 = |type_u8: u8,
+                          code_u8: u8,
+                          bytes5to8: [u8; 4]|
+         -> ArrayVec<u8, { Icmpv6Header::MAX_LEN }> {
+            #[rustfmt::skip]
+            let mut re = ArrayVec::from([
+                type_u8, code_u8, checksum_be[0], checksum_be[1],
+                bytes5to8[0], bytes5to8[1], bytes5to8[2], bytes5to8[3],
+
+                0, 0, 0, 0,
+                0, 0, 0, 0,
+                0, 0, 0, 0,
+                0, 0, 0, 0,
+
+                0, 0, 0, 0,
+                0, 0, 0, 0,
+                0, 0, 0, 0,
+                0, 0, 0, 0,
+            ]);
+            // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20.
+            unsafe {
+                re.set_len(8);
+            }
+            re
+        };
+
+        use crate::{icmpv6::*, Icmpv6Type::*};
+        match self.icmp_type {
+            Unknown {
+                type_u8,
+                code_u8,
+                bytes5to8,
+            } => return_4u8(type_u8, code_u8, bytes5to8),
+            DestinationUnreachable(header) => return_trivial(TYPE_DST_UNREACH, header.code_u8()),
+            PacketTooBig { mtu } => return_4u8(TYPE_PACKET_TOO_BIG, 0, mtu.to_be_bytes()),
+            TimeExceeded(code) => return_trivial(TYPE_TIME_EXCEEDED, code.code_u8()),
+            ParameterProblem(header) => return_4u8(
+                TYPE_PARAMETER_PROBLEM,
+                header.code.code_u8(),
+                header.pointer.to_be_bytes(),
+            ),
+            EchoRequest(echo) => return_4u8(TYPE_ECHO_REQUEST, 0, echo.to_bytes()),
+            EchoReply(echo) => return_4u8(TYPE_ECHO_REPLY, 0, echo.to_bytes()),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{
+        err::{ValueTooBigError, ValueType},
+        icmpv6::*,
+        test_gens::*,
+        *,
+    };
+    use alloc::{format, vec::Vec};
+    use arrayvec::ArrayVec;
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn new(icmp_type in icmpv6_type_any()) {
+            assert_eq!(
+                Icmpv6Header::new(icmp_type.clone()),
+                Icmpv6Header {
+                    icmp_type,
+                    checksum: 0,
+                }
+            );
+        }
+    }
+
+    proptest! {
+        #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
+        #[test]
+        fn with_checksum(
+            ip_header in ipv6_any(),
+            icmp_type in icmpv6_type_any(),
+            // max length is u32::MAX - header_len (7)
+            bad_len in (core::u32::MAX - 7) as usize..=(core::isize::MAX as usize),
+            payload in proptest::collection::vec(any::<u8>(), 0..1024)
+        ) {
+
+            // error case
+            {
+                // SAFETY: In case the error is not triggered
+                //         a segmentation fault will be triggered.
+                let too_big_slice = unsafe {
+                    //NOTE: The pointer must be initialized with a non null value
+                    //      otherwise a key constraint of slices is not fulfilled
+                    //      which can lead to crashes in release mode.
+                    use core::ptr::NonNull;
+                    core::slice::from_raw_parts(
+                        NonNull::<u8>::dangling().as_ptr(),
+                        bad_len
+                    )
+                };
+                assert_eq!(
+                    Icmpv6Header::with_checksum(icmp_type.clone(), ip_header.source, ip_header.destination, too_big_slice),
+                    Err(ValueTooBigError{
+                        actual: bad_len,
+                        max_allowed: (core::u32::MAX - 8) as usize,
+                        value_type: ValueType::Icmpv6PayloadLength,
+                    })
+                );
+            }
+
+            // non error case
+            assert_eq!(
+                Icmpv6Header::with_checksum(icmp_type.clone(), ip_header.source, ip_header.destination, &payload).unwrap(),
+                Icmpv6Header {
+                    icmp_type,
+                    checksum: icmp_type.calc_checksum(ip_header.source, ip_header.destination, &payload).unwrap(),
+                }
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            icmp_type in icmpv6_type_any(),
+            checksum in any::<u16>(),
+        ) {
+            let bytes = {
+                Icmpv6Header {
+                    icmp_type: icmp_type.clone(),
+                    checksum,
+                }.to_bytes()
+            };
+
+            // ok case
+            {
+                let result = Icmpv6Header::from_slice(&bytes).unwrap();
+                assert_eq!(
+                    Icmpv6Header{
+                        icmp_type,
+                        checksum,
+                    },
+                    result.0,
+                );
+                assert_eq!(&bytes[8..], result.1);
+            }
+
+
+            // size error case
+            for length in 0..8 {
+                assert_eq!(
+                    Icmpv6Header::from_slice(&bytes[..length]).unwrap_err(),
+                    err::LenError{
+                        required_len: bytes.len(),
+                        len: length,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::Icmpv6,
+                        layer_start_offset: 0
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read(
+            icmp_type in icmpv6_type_any(),
+            checksum in any::<u16>(),
+        ) {
+            let header = Icmpv6Header {
+                icmp_type: icmp_type.clone(),
+                checksum,
+            };
+            let bytes = header.to_bytes();
+
+            // ok case
+            {
+                let mut cursor = std::io::Cursor::new(&bytes);
+                let result = Icmpv6Header::read(&mut cursor).unwrap();
+                assert_eq!(header, result,);
+                assert_eq!(header.header_len() as u64, cursor.position());
+            }
+
+            // size error case
+            for length in 0..header.header_len() {
+                let mut cursor = std::io::Cursor::new(&bytes[..length]);
+                assert!(Icmpv6Header::read(&mut cursor).is_err());
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write(
+            icmp_type in icmpv6_type_any(),
+            checksum in any::<u16>(),
+            bad_len in 0..8usize
+        ) {
+            // normal case
+            {
+                let mut buffer = Vec::with_capacity(icmp_type.header_len());
+                let header = Icmpv6Header {
+                    icmp_type,
+                    checksum,
+                };
+                header.write(&mut buffer).unwrap();
+                assert_eq!(
+                    &header.to_bytes(),
+                    &buffer[..]
+                );
+            }
+
+            // error case
+            {
+                let mut buffer = [0u8;Icmpv6Header::MAX_LEN];
+                let mut writer = std::io::Cursor::new(&mut buffer[..bad_len]);
+                Icmpv6Header {
+                    icmp_type,
+                    checksum,
+                }.write(&mut writer).unwrap_err();
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header_len(icmp_type in icmpv6_type_any(), checksum in any::<u16>()) {
+            assert_eq!(
+                icmp_type.header_len(),
+                Icmpv6Header{
+                    icmp_type,
+                    checksum
+                }.header_len()
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn fixed_payload_size(icmp_type in icmpv6_type_any(), checksum in any::<u16>()) {
+            assert_eq!(
+                icmp_type.fixed_payload_size(),
+                Icmpv6Header{
+                    icmp_type,
+                    checksum
+                }.fixed_payload_size()
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
+        fn update_checksum(
+            ip_header in ipv6_any(),
+            icmp_type in icmpv6_type_any(),
+            start_checksum in any::<u16>(),
+            // max length is u32::MAX - header_len (7)
+            bad_len in (core::u32::MAX - 7) as usize..=(core::isize::MAX as usize),
+            payload in proptest::collection::vec(any::<u8>(), 0..1024)
+        ) {
+
+            // error case
+            {
+                // SAFETY: In case the error is not triggered
+                //         a segmentation fault will be triggered.
+                let too_big_slice = unsafe {
+                    //NOTE: The pointer must be initialized with a non null value
+                    //      otherwise a key constraint of slices is not fulfilled
+                    //      which can lead to crashes in release mode.
+                    use core::ptr::NonNull;
+                    core::slice::from_raw_parts(
+                        NonNull::<u8>::dangling().as_ptr(),
+                        bad_len
+                    )
+                };
+                assert_eq!(
+                    Icmpv6Header{
+                        icmp_type,
+                        checksum: 0
+                    }.update_checksum(ip_header.source, ip_header.destination, too_big_slice),
+                    Err(ValueTooBigError{
+                        actual: bad_len,
+                        max_allowed: (u32::MAX - 8) as usize,
+                        value_type: ValueType::Icmpv6PayloadLength
+                    })
+                );
+            }
+
+            // normal case
+            assert_eq!(
+                {
+                    let mut header = Icmpv6Header{
+                        icmp_type,
+                        checksum: start_checksum,
+                    };
+                    header.update_checksum(ip_header.source, ip_header.destination, &payload).unwrap();
+                    header
+                },
+                Icmpv6Header{
+                    icmp_type,
+                    checksum: icmp_type.calc_checksum(ip_header.source, ip_header.destination, &payload).unwrap(),
+                }
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_bytes(
+            checksum in any::<u16>(),
+            rand_u32 in any::<u32>(),
+            rand_4bytes in any::<[u8;4]>(),
+        ) {
+            use Icmpv6Type::*;
+
+            let with_5to8_bytes = |type_u8: u8, code_u8: u8, bytes5to8: [u8;4]| -> ArrayVec<u8, { Icmpv6Header::MAX_LEN }> {
+                let mut bytes = ArrayVec::<u8, { Icmpv6Header::MAX_LEN }>::new();
+                bytes.push(type_u8);
+                bytes.push(code_u8);
+                bytes.try_extend_from_slice(&checksum.to_be_bytes()).unwrap();
+                bytes.try_extend_from_slice(&bytes5to8).unwrap();
+                bytes
+            };
+
+            let simple_bytes = |type_u8: u8, code_u8: u8| -> ArrayVec<u8, { Icmpv6Header::MAX_LEN }> {
+                with_5to8_bytes(type_u8, code_u8, [0;4])
+            };
+
+            // destination unreachable
+            for (code, code_u8) in dest_unreachable_code_test_consts::VALID_VALUES {
+                assert_eq!(
+                    Icmpv6Header{
+                        icmp_type: DestinationUnreachable(code),
+                        checksum
+                    }.to_bytes(),
+                    simple_bytes(TYPE_DST_UNREACH, code_u8)
+                );
+            }
+
+            // packet too big
+            assert_eq!(
+                Icmpv6Header{
+                    icmp_type: PacketTooBig{ mtu: rand_u32 },
+                    checksum
+                }.to_bytes(),
+                with_5to8_bytes(TYPE_PACKET_TOO_BIG, 0, rand_u32.to_be_bytes())
+            );
+
+            // time exceeded
+            for (code, code_u8) in time_exceeded_code_test_consts::VALID_VALUES {
+                assert_eq!(
+                    Icmpv6Header{
+                        icmp_type: TimeExceeded(code),
+                        checksum
+                    }.to_bytes(),
+                    simple_bytes(TYPE_TIME_EXCEEDED, code_u8)
+                );
+            }
+
+            // parameter problem
+            for (code, code_u8) in parameter_problem_code_test_consts::VALID_VALUES {
+                assert_eq!(
+                    Icmpv6Header{
+                        icmp_type: ParameterProblem(
+                            ParameterProblemHeader{
+                                code,
+                                pointer: rand_u32,
+                            }
+                        ),
+                        checksum
+                    }.to_bytes(),
+                    with_5to8_bytes(TYPE_PARAMETER_PROBLEM, code_u8, rand_u32.to_be_bytes())
+                );
+            }
+
+            // echo request
+            assert_eq!(
+                Icmpv6Header{
+                    icmp_type: EchoRequest(IcmpEchoHeader {
+                        id: u16::from_be_bytes([rand_4bytes[0], rand_4bytes[1]]),
+                        seq: u16::from_be_bytes([rand_4bytes[2], rand_4bytes[3]]),
+                    }),
+                    checksum
+                }.to_bytes(),
+                with_5to8_bytes(TYPE_ECHO_REQUEST, 0, rand_4bytes)
+            );
+
+            // echo reply
+            assert_eq!(
+                Icmpv6Header{
+                    icmp_type: EchoReply(IcmpEchoHeader {
+                        id: u16::from_be_bytes([rand_4bytes[0], rand_4bytes[1]]),
+                        seq: u16::from_be_bytes([rand_4bytes[2], rand_4bytes[3]]),
+                    }),
+                    checksum
+                }.to_bytes(),
+                with_5to8_bytes(TYPE_ECHO_REPLY, 0, rand_4bytes)
+            );
+
+            // unknown
+            for type_u8 in 0..=u8::MAX {
+                for code_u8 in 0..=u8::MAX {
+                    assert_eq!(
+                        Icmpv6Header{
+                            icmp_type: Unknown {
+                                type_u8,
+                                code_u8,
+                                bytes5to8: rand_4bytes,
+                            },
+                            checksum
+                        }.to_bytes(),
+                        with_5to8_bytes(type_u8, code_u8, rand_4bytes)
+                    );
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn debug() {
+        let t = Icmpv6Type::Unknown {
+            type_u8: 0,
+            code_u8: 1,
+            bytes5to8: [2, 3, 4, 5],
+        };
+        assert_eq!(
+            format!(
+                "{:?}",
+                Icmpv6Header {
+                    icmp_type: t.clone(),
+                    checksum: 7
+                }
+            ),
+            format!("Icmpv6Header {{ icmp_type: {:?}, checksum: {:?} }}", t, 7)
+        );
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(icmp_type in icmpv6_type_any(), checksum in any::<u16>()) {
+            let header = Icmpv6Header{ icmp_type, checksum };
+            assert_eq!(header, header.clone());
+        }
+    }
+}
diff --git a/src/transport/icmpv6_slice.rs b/src/transport/icmpv6_slice.rs
new file mode 100644
index 0000000..23f9f53
--- /dev/null
+++ b/src/transport/icmpv6_slice.rs
@@ -0,0 +1,624 @@
+use crate::*;
+
+/// A slice containing an ICMPv6 network package.
+///
+/// Struct allows the selective read of fields in the ICMPv6
+/// packet.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Icmpv6Slice<'a> {
+    pub(crate) slice: &'a [u8],
+}
+
+impl<'a> Icmpv6Slice<'a> {
+    /// Creates a slice containing an ICMPv6 packet.
+    ///
+    /// # Errors
+    ///
+    /// The function will return an `Err` [`err::LenError`]
+    /// if the given slice is too small (smaller then [`Icmpv6Header::MIN_LEN`]) or
+    /// too large (bigger then [`icmpv6::MAX_ICMPV6_BYTE_LEN`]).
+    #[inline]
+    pub fn from_slice(slice: &'a [u8]) -> Result<Icmpv6Slice<'a>, err::LenError> {
+        //check length
+        if slice.len() < Icmpv6Header::MIN_LEN {
+            return Err(err::LenError {
+                required_len: Icmpv6Header::MIN_LEN,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::Icmpv6,
+                layer_start_offset: 0,
+            });
+        }
+        if slice.len() > icmpv6::MAX_ICMPV6_BYTE_LEN {
+            return Err(err::LenError {
+                required_len: icmpv6::MAX_ICMPV6_BYTE_LEN,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::Icmpv6,
+                layer_start_offset: 0,
+            });
+        }
+
+        //done
+        Ok(Icmpv6Slice { slice })
+    }
+
+    /// Decode the header fields and copy the results to a [`Icmpv6Header`] struct.
+    #[inline]
+    pub fn header(&self) -> Icmpv6Header {
+        Icmpv6Header {
+            icmp_type: self.icmp_type(),
+            checksum: self.checksum(),
+        }
+    }
+
+    /// Number of bytes/octets that will be converted into a
+    /// [`Icmpv6Header`] when [`Icmpv6Slice::header`] gets called.
+    #[inline]
+    pub fn header_len(&self) -> usize {
+        8
+    }
+
+    /// Decode the header values (excluding the checksum) into an [`Icmpv6Type`] enum.
+    pub fn icmp_type(&self) -> Icmpv6Type {
+        use crate::{icmpv6::*, Icmpv6Type::*};
+
+        match self.type_u8() {
+            TYPE_DST_UNREACH => {
+                if let Some(code) = DestUnreachableCode::from_u8(self.code_u8()) {
+                    return DestinationUnreachable(code);
+                }
+            }
+            TYPE_PACKET_TOO_BIG => {
+                if 0 == self.code_u8() {
+                    return PacketTooBig {
+                        mtu: u32::from_be_bytes(self.bytes5to8()),
+                    };
+                }
+            }
+            TYPE_TIME_EXCEEDED => {
+                if let Some(code) = TimeExceededCode::from_u8(self.code_u8()) {
+                    return TimeExceeded(code);
+                }
+            }
+            TYPE_PARAMETER_PROBLEM => {
+                if let Some(code) = ParameterProblemCode::from_u8(self.code_u8()) {
+                    return ParameterProblem(ParameterProblemHeader {
+                        code,
+                        pointer: u32::from_be_bytes(self.bytes5to8()),
+                    });
+                }
+            }
+            TYPE_ECHO_REQUEST => {
+                if 0 == self.code_u8() {
+                    return EchoRequest(IcmpEchoHeader::from_bytes(self.bytes5to8()));
+                }
+            }
+            TYPE_ECHO_REPLY => {
+                if 0 == self.code_u8() {
+                    return EchoReply(IcmpEchoHeader::from_bytes(self.bytes5to8()));
+                }
+            }
+            _ => {}
+        }
+        Unknown {
+            type_u8: self.type_u8(),
+            code_u8: self.code_u8(),
+            bytes5to8: self.bytes5to8(),
+        }
+    }
+
+    /// Returns "type" value in the ICMPv6 header.
+    #[inline]
+    pub fn type_u8(&self) -> u8 {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of Icmpv6Header::MIN_LEN (8).
+        unsafe { *self.slice.get_unchecked(0) }
+    }
+
+    /// Returns "code" value in the ICMPv6 header.
+    #[inline]
+    pub fn code_u8(&self) -> u8 {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of Icmpv6Header::MIN_LEN (8).
+        unsafe { *self.slice.get_unchecked(1) }
+    }
+
+    /// Returns "checksum" value in the ICMPv6 header.
+    #[inline]
+    pub fn checksum(&self) -> u16 {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of Icmpv6Header::MIN_LEN  (8).
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) }
+    }
+
+    /// Returns if the checksum in the slice is correct.
+    pub fn is_checksum_valid(&self, source_ip: [u8; 16], destination_ip: [u8; 16]) -> bool {
+        // NOTE: rfc4443 section 2.3 - Icmp6 *does* use a pseudoheader,
+        // unlike Icmp4
+        checksum::Sum16BitWords::new()
+            .add_16bytes(source_ip)
+            .add_16bytes(destination_ip)
+            .add_4bytes((self.slice().len() as u32).to_be_bytes())
+            .add_2bytes([0, ip_number::IPV6_ICMP.0])
+            // NOTE: From RFC 1071
+            // To check a checksum, the 1's complement sum is computed over the
+            // same set of octets, including the checksum field.  If the result
+            // is all 1 bits (-0 in 1's complement arithmetic), the check
+            // succeeds.
+            .add_slice(self.slice)
+            .ones_complement()
+            == 0
+    }
+
+    /// Returns the bytes from position 4 till and including the 8th position
+    /// in the ICMPv6 header.
+    ///
+    /// These bytes located at th 5th, 6th, 7th and 8th position of the ICMP
+    /// packet can depending on the ICMPv6 type and code contain additional data.
+    #[inline]
+    pub fn bytes5to8(&self) -> [u8; 4] {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of Icmpv6Header::MIN_LEN  (8).
+        unsafe {
+            [
+                *self.slice.get_unchecked(4),
+                *self.slice.get_unchecked(5),
+                *self.slice.get_unchecked(6),
+                *self.slice.get_unchecked(7),
+            ]
+        }
+    }
+
+    /// Returns the slice containing the ICMPv6 packet.
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+
+    /// Returns a slice to the bytes not covered by `.header()`.
+    #[inline]
+    pub fn payload(&self) -> &'a [u8] {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of Icmpv6Header::MIN_LEN(8).
+        unsafe { core::slice::from_raw_parts(self.slice.as_ptr().add(8), self.slice.len() - 8) }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{icmpv6::*, test_gens::*, Icmpv6Type::*, *};
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn from_slice(slice in proptest::collection::vec(any::<u8>(), 8..1024)) {
+            // ok case
+            assert_eq!(Icmpv6Slice::from_slice(&slice[..]).unwrap().slice(), &slice[..]);
+
+            // too small size error case
+            for len in 0..8 {
+                assert_eq!(
+                    Icmpv6Slice::from_slice(&slice[..len]).unwrap_err(),
+                    err::LenError{
+                        required_len: Icmpv6Header::MIN_LEN,
+                        len: len,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::Icmpv6,
+                        layer_start_offset: 0,
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        /// This error can only occur on systems with a pointer size
+        /// bigger then 64 bits.
+        #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
+        #[test]
+        fn from_slice_too_big_error(
+            bad_len in ((core::u32::MAX as usize) + 1)..=(core::isize::MAX as usize),
+        ) {
+            // too large packet error case
+            {
+                // SAFETY: In case the error is not triggered
+                //         a segmentation fault will be triggered.
+                let too_big_slice = unsafe {
+                    //NOTE: The pointer must be initialized with a non null value
+                    //      otherwise a key constraint of slices is not fulfilled
+                    //      which can lead to crashes in release mode.
+                    use core::ptr::NonNull;
+                    core::slice::from_raw_parts(
+                        NonNull::<u8>::dangling().as_ptr(),
+                        bad_len
+                    )
+                };
+                assert_eq!(
+                    Icmpv6Slice::from_slice(too_big_slice).unwrap_err(),
+                    err::LenError{
+                        required_len: u32::MAX as usize,
+                        len: bad_len,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::Icmpv6,
+                        layer_start_offset: 0
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header(
+            icmp_type in icmpv6_type_any(),
+            checksum in any::<u16>()
+        ) {
+            let expected = Icmpv6Header {
+                icmp_type,
+                checksum
+            };
+            assert_eq!(
+                Icmpv6Slice::from_slice(&expected.to_bytes()).unwrap().header(),
+                expected
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn icmp_type(
+            checksum in any::<[u8;2]>(),
+            bytes5to8 in any::<[u8;4]>()
+        ) {
+            use Icmpv6Type::*;
+
+            let gen_bytes = |type_u8: u8, code_u8: u8| -> [u8;8] {
+                [
+                    type_u8, code_u8, checksum[0], checksum[1],
+                    bytes5to8[0], bytes5to8[1], bytes5to8[2], bytes5to8[3]
+                ]
+            };
+
+            let assert_unknown = |type_u8: u8, code_u8: u8| {
+                assert_eq!(
+                    Icmpv6Slice::from_slice(&gen_bytes(type_u8, code_u8)).unwrap().icmp_type(),
+                    Unknown{
+                        type_u8,
+                        code_u8,
+                        bytes5to8,
+                    }
+                );
+            };
+
+            // destination unreachable
+            {
+                // known codes
+                for (code, code_u8) in dest_unreachable_code_test_consts::VALID_VALUES {
+                    assert_eq!(
+                        Icmpv6Slice::from_slice(&gen_bytes(TYPE_DST_UNREACH, code_u8)).unwrap().icmp_type(),
+                        DestinationUnreachable(code)
+                    );
+                }
+
+                // unknown codes
+                for code_u8 in 7..=u8::MAX {
+                    assert_unknown(TYPE_DST_UNREACH, code_u8);
+                }
+            }
+
+            // packet too big
+            {
+                // known code
+                assert_eq!(
+                    Icmpv6Slice::from_slice(&gen_bytes(TYPE_PACKET_TOO_BIG, 0)).unwrap().icmp_type(),
+                    PacketTooBig {
+                        mtu: u32::from_be_bytes(bytes5to8)
+                    }
+                );
+
+                // unknown code
+                for code_u8 in 1..=u8::MAX {
+                    assert_unknown(TYPE_PACKET_TOO_BIG, code_u8);
+                }
+            }
+
+            // time exceeded
+            {
+                // known codes
+                for (code, code_u8) in time_exceeded_code_test_consts::VALID_VALUES {
+                    assert_eq!(
+                        Icmpv6Slice::from_slice(&gen_bytes(TYPE_TIME_EXCEEDED, code_u8)).unwrap().icmp_type(),
+                        TimeExceeded(code)
+                    );
+                }
+
+                // unknown codes
+                for code_u8 in 2..=u8::MAX {
+                    assert_unknown(TYPE_TIME_EXCEEDED, code_u8);
+                }
+            }
+
+            // parameter problem
+            {
+                // known codes
+                for (code, code_u8) in parameter_problem_code_test_consts::VALID_VALUES {
+                    assert_eq!(
+                        Icmpv6Slice::from_slice(&gen_bytes(TYPE_PARAMETER_PROBLEM, code_u8)).unwrap().icmp_type(),
+                        ParameterProblem(ParameterProblemHeader{
+                            code,
+                            pointer: u32::from_be_bytes(bytes5to8),
+                        })
+                    );
+                }
+
+                // unknown codes
+                for code_u8 in 11..=u8::MAX {
+                    assert_unknown(TYPE_PARAMETER_PROBLEM, code_u8);
+                }
+            }
+
+            // echo request
+            {
+                // known code
+                assert_eq!(
+                    Icmpv6Slice::from_slice(&gen_bytes(TYPE_ECHO_REQUEST, 0)).unwrap().icmp_type(),
+                    EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8))
+                );
+
+                // unknown codes
+                for code_u8 in 1..=u8::MAX {
+                    assert_unknown(TYPE_ECHO_REPLY, code_u8);
+                }
+            }
+
+            // echo reply
+            {
+                // known code
+                assert_eq!(
+                    Icmpv6Slice::from_slice(&gen_bytes(TYPE_ECHO_REPLY, 0)).unwrap().icmp_type(),
+                    EchoReply(IcmpEchoHeader::from_bytes(bytes5to8))
+                );
+
+                // unknown codes
+                for code_u8 in 1..=u8::MAX {
+                    assert_unknown(TYPE_ECHO_REPLY, code_u8);
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header_len(
+            code_u8 in any::<u8>(),
+            bytes5to8 in any::<[u8;4]>(),
+        ) {
+            let len_8_types = [
+                DestinationUnreachable(DestUnreachableCode::Prohibited),
+                PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), },
+                TimeExceeded(TimeExceededCode::HopLimitExceeded),
+                ParameterProblem(
+                    ParameterProblemHeader{
+                        code: ParameterProblemCode::OptionTooBig,
+                        pointer: u32::from_be_bytes(bytes5to8),
+                    }
+                ),
+                EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)),
+                EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)),
+            ];
+
+            for t in len_8_types {
+                assert_eq!(
+                    t.header_len(),
+                    Icmpv6Slice::from_slice(
+                        &Icmpv6Header::new(t).to_bytes()
+                    ).unwrap().header_len()
+                );
+            }
+
+            for t in 0..=u8::MAX {
+                let header = Icmpv6Header::new(
+                    Unknown{
+                        type_u8: t,
+                        code_u8,
+                        bytes5to8,
+                    }
+                );
+                assert_eq!(
+                    8,
+                    Icmpv6Slice::from_slice(
+                        &header.to_bytes()
+                    ).unwrap().header_len()
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn type_u8(slice in proptest::collection::vec(any::<u8>(), 8..16)) {
+            assert_eq!(
+                Icmpv6Slice::from_slice(&slice[..]).unwrap().type_u8(),
+                slice[0]
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn code_u8(slice in proptest::collection::vec(any::<u8>(), 8..16)) {
+            assert_eq!(
+                Icmpv6Slice::from_slice(&slice[..]).unwrap().code_u8(),
+                slice[1]
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn checksum(slice in proptest::collection::vec(any::<u8>(), 8..16)) {
+            assert_eq!(
+                Icmpv6Slice::from_slice(&slice[..]).unwrap().checksum(),
+                u16::from_be_bytes([slice[2], slice[3]])
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn is_checksum_valid(
+            ip_header in ipv6_any(),
+            icmp_type in icmpv6_type_any(),
+            payload in proptest::collection::vec(any::<u8>(), 0..1024),
+            flip_byte in 0usize..1032,
+        ) {
+            // generate slice with a correct checksum
+            let header = Icmpv6Header::with_checksum(icmp_type, ip_header.source, ip_header.destination, &payload).unwrap();
+            let bytes = {
+                let mut bytes = Vec::with_capacity(header.header_len() + payload.len());
+                header.write(&mut bytes).unwrap();
+                bytes.extend_from_slice(&payload);
+                bytes
+            };
+
+            // check that the checksum gets reported as ok
+            assert!(
+                Icmpv6Slice::from_slice(&bytes).unwrap().is_checksum_valid(ip_header.source, ip_header.destination)
+            );
+
+            // corrupt icmp packet
+            {
+                let mut corrupted_bytes = bytes.clone();
+                let i = flip_byte % corrupted_bytes.len();
+                corrupted_bytes[i] = !corrupted_bytes[i];
+
+                assert_eq!(
+                    false,
+                    Icmpv6Slice::from_slice(&corrupted_bytes).unwrap().is_checksum_valid(ip_header.source, ip_header.destination)
+                );
+            }
+
+            // corrupt ip source
+            {
+                let mut corrupted_source = ip_header.source;
+                let i = flip_byte % corrupted_source.len();
+                corrupted_source[i] = !corrupted_source[i];
+
+                assert_eq!(
+                    false,
+                    Icmpv6Slice::from_slice(&bytes).unwrap().is_checksum_valid(corrupted_source, ip_header.destination)
+                );
+            }
+
+            // corrupt ip destination
+            {
+                let mut corrupted_dest = ip_header.destination;
+                let i = flip_byte % corrupted_dest.len();
+                corrupted_dest[i] = !corrupted_dest[i];
+
+                assert_eq!(
+                    false,
+                    Icmpv6Slice::from_slice(&bytes).unwrap().is_checksum_valid(ip_header.source, corrupted_dest)
+                );
+            }
+
+            // corrupt length
+            {
+                let mut larger_bytes = bytes.clone();
+                larger_bytes.push(0);
+                larger_bytes.push(0);
+
+                assert_eq!(
+                    false,
+                    Icmpv6Slice::from_slice(&larger_bytes).unwrap().is_checksum_valid(ip_header.source, ip_header.destination)
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn bytes5to8(slice in proptest::collection::vec(any::<u8>(), 8..16)) {
+            assert_eq!(
+                Icmpv6Slice::from_slice(&slice[..]).unwrap().bytes5to8(),
+                [slice[4], slice[5], slice[6], slice[7]]
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn slice(slice in proptest::collection::vec(any::<u8>(), 8..16)) {
+            assert_eq!(
+                Icmpv6Slice::from_slice(&slice[..]).unwrap().slice(),
+                &slice[..]
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn payload(
+            type_u8 in any::<u8>(),
+            code_u8 in any::<u8>(),
+            bytes5to8 in any::<[u8;4]>(),
+            payload in proptest::collection::vec(any::<u8>(), 8..16)
+        ) {
+            let len_8_types = [
+                Unknown{
+                    type_u8,
+                    code_u8,
+                    bytes5to8,
+                },
+                DestinationUnreachable(DestUnreachableCode::Prohibited),
+                PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), },
+                TimeExceeded(TimeExceededCode::HopLimitExceeded),
+                ParameterProblem(
+                    ParameterProblemHeader{
+                        code: ParameterProblemCode::ExtensionHeaderChainTooLong,
+                        pointer: u32::from_be_bytes(bytes5to8),
+                    }
+                ),
+                EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)),
+                EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)),
+            ];
+
+            for t in len_8_types {
+                let mut bytes = Vec::with_capacity(t.header_len() + payload.len());
+                Icmpv6Header::new(t.clone()).write(&mut bytes).unwrap();
+                bytes.extend_from_slice(&payload);
+
+                assert_eq!(
+                    Icmpv6Slice::from_slice(&bytes[..]).unwrap().payload(),
+                    &payload[..]
+                );
+            }
+        }
+    }
+
+    #[test]
+    fn debug() {
+        let data = [0u8; 8];
+        assert_eq!(
+            format!("{:?}", Icmpv6Slice::from_slice(&data).unwrap()),
+            format!("Icmpv6Slice {{ slice: {:?} }}", &data)
+        );
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(slice in proptest::collection::vec(any::<u8>(), 8..16)) {
+            assert_eq!(
+                Icmpv6Slice::from_slice(&slice).unwrap().clone(),
+                Icmpv6Slice::from_slice(&slice).unwrap()
+            );
+        }
+    }
+}
diff --git a/src/transport/icmpv6_type.rs b/src/transport/icmpv6_type.rs
new file mode 100644
index 0000000..f36393a
--- /dev/null
+++ b/src/transport/icmpv6_type.rs
@@ -0,0 +1,851 @@
+use crate::{
+    err::{ValueTooBigError, ValueType},
+    *,
+};
+
+/// Different kinds of ICMPv6 messages.
+///
+/// The data stored in this enum corresponds to the statically sized data
+/// at the start of an ICMPv6 packet without the checksum. If you also need
+/// the checksum you can package and [`Icmpv6Type`] value in an [`Icmpv6Header`]
+/// struct.
+///
+/// # Decoding Example (complete packet):
+///
+/// ```
+/// # use etherparse::{PacketBuilder};
+/// # let mut builder = PacketBuilder::
+/// #   ethernet2([0;6], [0;6])
+/// #   .ipv6([0;16], [0;16], 20)
+/// #   .icmpv6_echo_request(1, 2);
+/// # let payload = [1,2,3,4];
+/// # let mut packet = Vec::<u8>::with_capacity(builder.size(payload.len()));
+/// # builder.write(&mut packet, &payload);
+/// use etherparse::PacketHeaders;
+///
+/// let headers = PacketHeaders::from_ethernet_slice(&packet).unwrap();
+///
+/// use etherparse::TransportHeader::*;
+/// match headers.transport {
+///     Some(Icmpv6(icmp)) => {
+///         use etherparse::Icmpv6Type::*;
+///         match icmp.icmp_type {
+///             // Unknown is used when further decoding is currently not supported for the icmp type & code.
+///             // You can still further decode the packet on your own by using the raw data in this enum
+///             // together with `headers.payload` (contains the packet data after the 8th byte)
+///             Unknown{ type_u8, code_u8, bytes5to8 } => println!("Unknown{{ type_u8: {}, code_u8: {}, bytes5to8: {:?} }}", type_u8, code_u8, bytes5to8),
+///             DestinationUnreachable(header) => println!("{:?}", header),
+///             PacketTooBig { mtu } => println!("TimeExceeded{{ mtu: {} }}", mtu),
+///             TimeExceeded(code) => println!("{:?}", code),
+///             ParameterProblem(header) => println!("{:?}", header),
+///             EchoRequest(header) => println!("{:?}", header),
+///             EchoReply(header) => println!("{:?}", header),
+///         }
+///     },
+///     _ => {},
+/// }
+/// ```
+///
+/// # Encoding Example (only ICMPv6 part)
+///
+/// To get the on wire bytes of an Icmpv6Type it needs to get packaged
+/// into a [`Icmpv6Header`] so the checksum gets calculated.
+///
+/// ```
+/// # use etherparse::Ipv6Header;
+/// # let ip_header: Ipv6Header = Default::default();
+/// # let invoking_packet : [u8;0] = [];
+///
+/// use etherparse::{Icmpv6Type, icmpv6::DestUnreachableCode};
+/// let t = Icmpv6Type::DestinationUnreachable(
+///     DestUnreachableCode::Address
+/// );
+///
+/// // to calculate the checksum the ip header and the payload
+/// // (in case of dest unreachable the invoking packet) are needed
+/// let header = t.to_header(ip_header.source, ip_header.destination, &invoking_packet).unwrap();
+///
+/// // an ICMPv6 packet is composed of the header and payload
+/// let mut packet = Vec::with_capacity(header.header_len() + invoking_packet.len());
+/// packet.extend_from_slice(&header.to_bytes());
+/// packet.extend_from_slice(&invoking_packet);
+/// #
+/// # {
+/// #   let checksum_be = header.checksum.to_be_bytes();
+/// #   assert_eq!(
+/// #       &packet,
+/// #       &[
+/// #           header.icmp_type.type_u8(),
+/// #           header.icmp_type.code_u8(),
+/// #           checksum_be[0],
+/// #           checksum_be[1],
+/// #           0,0,0,0
+/// #       ]
+/// #   );
+/// # }
+/// ```
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+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.
+    ///
+    /// # What is part of the header for `Icmpv6Type::Unknown`?
+    ///
+    /// For unknown ICMPv6 type & code combination the first 8 bytes are stored
+    /// in the [`Icmpv6Header`] and the rest is stored in the payload
+    /// ([`Icmpv6Slice::payload`] or [`PacketHeaders::payload`]).
+    ///
+    ///
+    /// ```text
+    /// 0               1               2               3               4
+    /// +---------------------------------------------------------------+  -
+    /// |     type_u8   |    code_u8    |  checksum (in Icmpv6Header)   |  |
+    /// +---------------------------------------------------------------+  | part of header & type
+    /// |                          bytes5to8                            |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// |                                                               |  |
+    /// ...                           ...                             ...  | part of payload
+    /// |                                                               |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// ```
+    Unknown {
+        /// ICMPv6 type (present in the first byte of the ICMPv6 packet).
+        type_u8: u8,
+        /// ICMPv6 code (present in the 2nd byte of the ICMPv6 packet).
+        code_u8: u8,
+        /// Bytes located at th 5th, 6th, 7th and 8th position of the ICMP packet.
+        bytes5to8: [u8; 4],
+    },
+
+    /// Message sent to inform the client that the destination is unreachable for
+    /// some reason.
+    ///
+    /// # What is part of the header for `Icmpv6Type::DestinationUnreachable`?
+    ///
+    /// For the `Icmpv6Type::DestinationUnreachable` type the first 8 bytes/octets of the ICMPv6
+    /// packet are part of the header. The `unused` part is not stored and droped.
+    /// The offending packet is stored in the payload part of the packet
+    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
+    /// the [`Icmpv6Header`].
+    ///
+    /// ```text
+    /// 0               1               2               3               4
+    /// +---------------------------------------------------------------+  -
+    /// |       1       | [value as u8] |  checksum (in Icmpv6Header)   |  |
+    /// +---------------------------------------------------------------+  | part of header & type
+    /// |                           <unused>                            |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// |                                                               |  |
+    /// |     <As much of invoking packet as possible without           |  | part of payload
+    /// ...   the ICMPv6 packet exceeding the minimum IPv6 MTU>       ...  |
+    /// |                                                               |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// ```
+    ///
+    /// # RFC 4443 Description
+    ///
+    /// A Destination Unreachable message SHOULD be generated by a router, or
+    /// by the IPv6 layer in the originating node, in response to a packet
+    /// 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.)
+    DestinationUnreachable(icmpv6::DestUnreachableCode),
+
+    /// Sent if a packet to too big to be forwarded.
+    ///
+    /// # What is part of the header for `Icmpv6Type::PacketTooBig`?
+    ///
+    /// For the `Icmpv6Type::PacketTooBig` type the first 8 bytes/octets of the ICMPv6
+    /// packet are part of the header. The offending packet is stored in the payload part of the packet
+    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
+    /// the [`Icmpv6Header`].
+    ///
+    /// ```text
+    /// 0               1               2               3               4
+    /// +---------------------------------------------------------------+  -
+    /// |       2       |       0       |  checksum (in Icmpv6Header)   |  |
+    /// +---------------------------------------------------------------+  | part of header & type
+    /// |                              mtu                              |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// |                                                               |  |
+    /// |     <As much of invoking packet as possible without           |  | part of payload
+    /// ...   the ICMPv6 packet exceeding the minimum IPv6 MTU>       ...  |
+    /// |                                                               |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// ```
+    ///
+    /// # RFC 4443 Description
+    ///
+    /// A Packet Too Big MUST be sent by a router in response to a packet
+    /// that it cannot forward because the packet is larger than the MTU of
+    /// the outgoing link.  The information in this message is used as part
+    /// of the Path MTU Discovery process.
+    PacketTooBig {
+        /// The Maximum Transmission Unit of the next-hop link.
+        mtu: u32,
+    },
+
+    /// Generated when a datagram had to be discarded due to the hop limit field
+    /// reaching zero.
+    ///
+    /// # What is part of the header for `Icmpv6Type::TimeExceeded`?
+    ///
+    /// For the `Icmpv6Type::TimeExceeded` type the first 8 bytes/octets of the ICMPv6
+    /// packet are part of the header. The `unused` part is not stored and droped.
+    /// The offending packet is stored in the payload part of the packet
+    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
+    /// the [`Icmpv6Header`].
+    ///
+    /// ```text
+    /// 0               1               2               3               4
+    /// +---------------------------------------------------------------+  -
+    /// |       3       | [value as u8] |  checksum (in Icmpv6Header)   |  |
+    /// +---------------------------------------------------------------+  | part of header & type
+    /// |                           <unused>                            |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// |                                                               |  |
+    /// |     <As much of invoking packet as possible without           |  | part of payload
+    /// ...   the ICMPv6 packet exceeding the minimum IPv6 MTU>       ...  |
+    /// |                                                               |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// ```
+    ///
+    /// # RFC 4443 Description
+    ///
+    /// If a router receives a packet with a Hop Limit of zero, or if a
+    /// router decrements a packet's Hop Limit to zero, it MUST discard the
+    /// packet and originate an ICMPv6 Time Exceeded message with Code 0 to
+    /// the source of the packet.  This indicates either a routing loop or
+    /// too small an initial Hop Limit value.
+    ///
+    /// An ICMPv6 Time Exceeded message with Code 1 is used to report
+    /// fragment reassembly timeout, as specified in [IPv6, Section 4.5].
+    TimeExceeded(icmpv6::TimeExceededCode),
+
+    /// Sent if there is a problem with a parameter in a received packet.
+    ///
+    /// # What is part of the header for `Icmpv6Type::ParameterProblem`?
+    ///
+    /// For the `Icmpv6Type::ParameterProblem` type the first 8 bytes/octets of the ICMPv6
+    /// packet are part of the header. The `unused` part is not stored and droped.
+    /// The offending packet is stored in the payload part of the packet
+    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of
+    /// the [`Icmpv6Header`].
+    ///
+    /// ```text
+    /// 0               1               2               3               4
+    /// +---------------------------------------------------------------+  -
+    /// |       4       | [value].code  |  checksum (in Icmpv6Header)   |  |
+    /// +---------------------------------------------------------------+  | part of header & type
+    /// |                        [value].pointer                        |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// |                                                               |  |
+    /// |     <As much of invoking packet as possible without           |  | part of payload
+    /// ...   the ICMPv6 packet exceeding the minimum IPv6 MTU>       ...  |
+    /// |                                                               |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// ```
+    ///
+    /// # RFC 4443 Description
+    ///
+    /// If an IPv6 node processing a packet finds a problem with a field in
+    /// the IPv6 header or extension headers such that it cannot complete
+    /// processing the packet, it MUST discard the packet and SHOULD
+    /// originate an ICMPv6 Parameter Problem message to the packet's source,
+    /// indicating the type and location of the problem.
+    ParameterProblem(icmpv6::ParameterProblemHeader),
+
+    /// Requesting an `EchoReply` from the receiver.
+    ///
+    /// # What is part of the header for `Icmpv6Type::EchoRequest`?
+    ///
+    /// For the [`Icmpv6Type::EchoRequest`] type the first 8 bytes/octets of the
+    /// ICMPv6 packet are part of the header. This includes the `id` and `seq`
+    /// fields. The data part of the ICMP echo request packet is part of the payload
+    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the
+    /// [`Icmpv6Header`].
+    ///
+    /// ```text
+    /// 0               1               2               3               4
+    /// +---------------------------------------------------------------+  -
+    /// |      128      |       0       |  checksum (in Icmpv6Header)   |  |
+    /// +---------------------------------------------------------------+  | part of header & type
+    /// |          [value].id           |         [value].seq           |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// |                                                               |  |
+    /// ...                          <data>                           ...  | part of payload
+    /// |                                                               |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// ```
+    ///
+    /// # RFC 4443 Description
+    ///
+    /// Every node MUST implement an ICMPv6 Echo responder function that
+    /// receives Echo Requests and originates corresponding Echo Replies.  A
+    /// node SHOULD also implement an application-layer interface for
+    /// originating Echo Requests and receiving Echo Replies, for diagnostic
+    /// purposes.
+    EchoRequest(IcmpEchoHeader),
+    /// Response to an `EchoRequest` message.
+    ///
+    /// # What is part of the header for `Icmpv6Type::EchoReply`?
+    ///
+    /// For the [`Icmpv6Type::EchoReply`] type the first 8 bytes/octets of the
+    /// ICMPv6 packet are part of the header. This includes the `id` and `seq`
+    /// fields. The data part of the ICMP echo request packet is part of the payload
+    /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the
+    /// [`Icmpv6Header`].
+    ///
+    /// ```text
+    /// 0               1               2               3               4
+    /// +---------------------------------------------------------------+  -
+    /// |      129      |       0       |  checksum (in Icmpv6Header)   |  |
+    /// +---------------------------------------------------------------+  | part of header & type
+    /// |          [value].id           |         [value].seq           |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// |                                                               |  |
+    /// ...                          <data>                           ...  | part of payload
+    /// |                                                               |  ↓
+    /// +---------------------------------------------------------------+  -
+    /// ```
+    ///
+    /// # RFC 4443 Description
+    ///
+    /// Every node MUST implement an ICMPv6 Echo responder function that
+    /// receives Echo Requests and originates corresponding Echo Replies. A
+    /// node SHOULD also implement an application-layer interface for
+    /// originating Echo Requests and receiving Echo Replies, for diagnostic
+    /// purposes.
+    ///
+    /// The source address of an Echo Reply sent in response to a unicast
+    /// Echo Request message MUST be the same as the destination address of
+    /// that Echo Request message.
+    ///
+    /// An Echo Reply SHOULD be sent in response to an Echo Request message
+    /// sent to an IPv6 multicast or anycast address.  In this case, the
+    /// source address of the reply MUST be a unicast address belonging to
+    /// the interface on which the Echo Request message was received.
+    ///
+    /// The data received in the ICMPv6 Echo Request message MUST be returned
+    /// entirely and unmodified in the ICMPv6 Echo Reply message.
+    EchoReply(IcmpEchoHeader),
+}
+
+impl Icmpv6Type {
+    /// Returns the type value (first byte of the ICMPv6 header) of this type.
+    #[inline]
+    pub fn type_u8(&self) -> u8 {
+        use crate::{icmpv6::*, Icmpv6Type::*};
+        match self {
+            Unknown {
+                type_u8,
+                code_u8: _,
+                bytes5to8: _,
+            } => *type_u8,
+            DestinationUnreachable(_) => TYPE_DST_UNREACH,
+            PacketTooBig { mtu: _ } => TYPE_PACKET_TOO_BIG,
+            TimeExceeded(_) => TYPE_TIME_EXCEEDED,
+            ParameterProblem(_) => TYPE_PARAMETER_PROBLEM,
+            EchoRequest(_) => TYPE_ECHO_REQUEST,
+            EchoReply(_) => TYPE_ECHO_REPLY,
+        }
+    }
+
+    /// Returns the code value (second byte of the ICMPv6 header) of this type.
+    #[inline]
+    pub fn code_u8(&self) -> u8 {
+        use Icmpv6Type::*;
+        match self {
+            Unknown {
+                type_u8: _,
+                code_u8,
+                bytes5to8: _,
+            } => *code_u8,
+            DestinationUnreachable(code) => code.code_u8(),
+            PacketTooBig { mtu: _ } => 0,
+            TimeExceeded(code) => code.code_u8(),
+            ParameterProblem(header) => header.code.code_u8(),
+            EchoRequest(_) => 0,
+            EchoReply(_) => 0,
+        }
+    }
+
+    /// Calculates the checksum of the ICMPv6 header.
+    ///
+    /// <p style="background:rgba(255,181,77,0.16);padding:0.75em;">
+    /// <strong>Warning:</strong> Don't use this method to verfy if a checksum of a
+    /// received packet is correct. This method assumes that all unused bytes are
+    /// filled with zeros. If this is not the case the computed checksum value will
+    /// will be incorrect for a received packet.
+    ///
+    /// If you want to verify that a received packet has a correct checksum use
+    /// [`Icmpv6Slice::is_checksum_valid`] instead.
+    /// </p>
+    pub fn calc_checksum(
+        &self,
+        source_ip: [u8; 16],
+        destination_ip: [u8; 16],
+        payload: &[u8],
+    ) -> Result<u16, ValueTooBigError<usize>> {
+        // check that the total length fits into the field
+        //
+        // Note according to RFC 2460 the "Upper-Layer Packet Length" used
+        // in the checksum calculation, for protocols that don't contain
+        // their own length information (like ICMPv6), is "the Payload Length
+        // from the IPv6 header, minus the length of any extension headers present
+        // between the IPv6 header and the upper-layer header."
+        let max_payload_len: usize = (u32::MAX as usize) - self.header_len();
+        if max_payload_len < payload.len() {
+            return Err(ValueTooBigError {
+                actual: payload.len(),
+                max_allowed: max_payload_len,
+                value_type: ValueType::Icmpv6PayloadLength,
+            });
+        }
+
+        let msg_len = payload.len() + self.header_len();
+
+        // calculate the checksum
+        // NOTE: rfc4443 section 2.3 - Icmp6 *does* use a pseudoheader,
+        // unlike Icmp4
+        let pseudo_sum = checksum::Sum16BitWords::new()
+            .add_16bytes(source_ip)
+            .add_16bytes(destination_ip)
+            .add_2bytes([0, ip_number::IPV6_ICMP.0])
+            .add_4bytes((msg_len as u32).to_be_bytes());
+
+        use crate::{icmpv6::*, Icmpv6Type::*};
+        Ok(match self {
+            Unknown {
+                type_u8,
+                code_u8,
+                bytes5to8,
+            } => pseudo_sum
+                .add_2bytes([*type_u8, *code_u8])
+                .add_4bytes(*bytes5to8),
+            DestinationUnreachable(header) => {
+                pseudo_sum.add_2bytes([TYPE_DST_UNREACH, header.code_u8()])
+            }
+            PacketTooBig { mtu } => pseudo_sum
+                .add_2bytes([TYPE_PACKET_TOO_BIG, 0])
+                .add_4bytes(mtu.to_be_bytes()),
+            TimeExceeded(code) => pseudo_sum.add_2bytes([TYPE_TIME_EXCEEDED, code.code_u8()]),
+            ParameterProblem(header) => pseudo_sum
+                .add_2bytes([TYPE_PARAMETER_PROBLEM, header.code.code_u8()])
+                .add_4bytes(header.pointer.to_be_bytes()),
+            EchoRequest(echo) => pseudo_sum
+                .add_2bytes([TYPE_ECHO_REQUEST, 0])
+                .add_4bytes(echo.to_bytes()),
+            EchoReply(echo) => pseudo_sum
+                .add_2bytes([TYPE_ECHO_REPLY, 0])
+                .add_4bytes(echo.to_bytes()),
+        }
+        .add_slice(payload)
+        .ones_complement()
+        .to_be())
+    }
+
+    /// Creates a header with the correct checksum.
+    pub fn to_header(
+        self,
+        source_ip: [u8; 16],
+        destination_ip: [u8; 16],
+        payload: &[u8],
+    ) -> Result<Icmpv6Header, ValueTooBigError<usize>> {
+        Ok(Icmpv6Header {
+            checksum: self.calc_checksum(source_ip, destination_ip, payload)?,
+            icmp_type: self,
+        })
+    }
+
+    /// Serialized length of the header in bytes/octets.
+    ///
+    /// Note that this size is not the size of the entire
+    /// ICMPv6 packet but only the header.
+    pub fn header_len(&self) -> usize {
+        use Icmpv6Type::*;
+        match self {
+            Unknown {
+                type_u8: _,
+                code_u8: _,
+                bytes5to8: _,
+            }
+            | DestinationUnreachable(_)
+            | PacketTooBig { mtu: _ }
+            | TimeExceeded(_)
+            | ParameterProblem(_)
+            | EchoRequest(_)
+            | EchoReply(_) => 8,
+        }
+    }
+
+    /// If the ICMP type has a fixed size returns the number of
+    /// bytes that should be present after the header of this type.
+    #[inline]
+    pub fn fixed_payload_size(&self) -> Option<usize> {
+        use Icmpv6Type::*;
+        match self {
+            Unknown {
+                type_u8: _,
+                code_u8: _,
+                bytes5to8: _,
+            }
+            | DestinationUnreachable(_)
+            | PacketTooBig { mtu: _ }
+            | TimeExceeded(_)
+            | ParameterProblem(_)
+            | EchoRequest(_)
+            | EchoReply(_) => None,
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{
+        err::{ValueTooBigError, ValueType},
+        icmpv6::*,
+        test_gens::*,
+        Icmpv6Type::*,
+        *,
+    };
+    use alloc::format;
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn type_u8(
+            code_u8 in any::<u8>(),
+            bytes5to8 in any::<[u8;4]>(),
+        ) {
+            {
+                let type_u8_type_pair = [
+                    (TYPE_DST_UNREACH, DestinationUnreachable(DestUnreachableCode::SourceAddressFailedPolicy)),
+                    (TYPE_PACKET_TOO_BIG, PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), }),
+                    (TYPE_TIME_EXCEEDED, TimeExceeded(TimeExceededCode::HopLimitExceeded)),
+                    (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))),
+                ];
+                for test in type_u8_type_pair {
+                    assert_eq!(test.0, test.1.type_u8());
+                }
+            }
+
+            for t in 0..=u8::MAX {
+                assert_eq!(
+                    t,
+                    Unknown{
+                        type_u8: t,
+                        code_u8,
+                        bytes5to8,
+                    }.type_u8()
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn code_u8(
+            code_u8 in any::<u8>(),
+            bytes5to8 in any::<[u8;4]>(),
+        ) {
+            // types with 0 as code
+            {
+                let code_type_pair = [
+                    (0, PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), }),
+                    (0, EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8))),
+                    (0, EchoReply(IcmpEchoHeader::from_bytes(bytes5to8))),
+                ];
+                for test in code_type_pair {
+                    assert_eq!(test.0, test.1.code_u8());
+                }
+            }
+
+            // destination unreachable
+            for (code, code_u8) in dest_unreachable_code_test_consts::VALID_VALUES {
+                assert_eq!(code_u8, DestinationUnreachable(code).code_u8());
+            }
+
+            // time exceeded
+            for (code, code_u8) in time_exceeded_code_test_consts::VALID_VALUES {
+                assert_eq!(code_u8, TimeExceeded(code).code_u8());
+            }
+
+            // parameter problem
+            for (code, code_u8) in parameter_problem_code_test_consts::VALID_VALUES {
+                assert_eq!(
+                    code_u8,
+                    ParameterProblem(
+                        ParameterProblemHeader{
+                            code,
+                            pointer: u32::from_be_bytes(bytes5to8),
+                        }
+                    ).code_u8()
+                );
+            }
+
+            // unknown
+            for t in 0..=u8::MAX {
+                assert_eq!(
+                    code_u8,
+                    Unknown{
+                        type_u8: t,
+                        code_u8,
+                        bytes5to8,
+                    }.code_u8()
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        #[cfg(not(target_pointer_width = "32"))]
+        fn calc_checksum(
+            ip_header in ipv6_any(),
+            icmpv6_type in icmpv6_type_any(),
+            type_u8 in any::<u8>(),
+            code_u8 in any::<u8>(),
+            bytes5to8 in any::<[u8;4]>(),
+            // max length is u32::MAX - header_len (7)
+            bad_len in (core::u32::MAX - 7) as usize..=(core::isize::MAX as usize),
+            payload in proptest::collection::vec(any::<u8>(), 0..64)
+        ) {
+            use Icmpv6Type::*;
+
+            // size error case
+            {
+                // SAFETY: In case the error is not triggered
+                //         a segmentation fault will be triggered.
+                let too_big_slice = unsafe {
+                    //NOTE: The pointer must be initialized with a non null value
+                    //      otherwise a key constraint of slices is not fulfilled
+                    //      which can lead to crashes in release mode.
+                    use core::ptr::NonNull;
+                    core::slice::from_raw_parts(
+                        NonNull::<u8>::dangling().as_ptr(),
+                        bad_len
+                    )
+                };
+                assert_eq!(
+                    icmpv6_type.calc_checksum(ip_header.source, ip_header.destination, too_big_slice),
+                    Err(ValueTooBigError{
+                        actual: bad_len,
+                        max_allowed: (core::u32::MAX - 8) as usize,
+                        value_type: ValueType::Icmpv6PayloadLength
+                    })
+                );
+            }
+
+            // normal cases
+            {
+                let test_checksum_calc = |icmp_type: Icmpv6Type| {
+                    let expected_checksum = {
+                        crate::checksum::Sum16BitWords::new()
+                        .add_16bytes(ip_header.source)
+                        .add_16bytes(ip_header.destination)
+                        .add_2bytes([0, ip_number::IPV6_ICMP.0])
+                        .add_4bytes((
+                            payload.len() as u32 + icmpv6_type.header_len() as u32
+                        ).to_be_bytes())
+                        .add_slice(&Icmpv6Header {
+                            icmp_type: icmp_type.clone(),
+                            checksum: 0 // use zero so the checksum gets correct calculated
+                        }.to_bytes())
+                        .add_slice(&payload)
+                        .ones_complement()
+                        .to_be()
+                    };
+                    assert_eq!(
+                        expected_checksum,
+                        icmp_type.calc_checksum(
+                            ip_header.source,
+                            ip_header.destination,
+                            &payload
+                        ).unwrap()
+                    );
+                };
+
+                // unknown
+                test_checksum_calc(
+                    Unknown{
+                        type_u8, code_u8, bytes5to8
+                    }
+                );
+
+                // destination unreachable
+                for (code, _) in dest_unreachable_code_test_consts::VALID_VALUES {
+                    test_checksum_calc(DestinationUnreachable(code));
+                }
+
+                // packet too big
+                test_checksum_calc(PacketTooBig{
+                    mtu: u32::from_be_bytes(bytes5to8)
+                });
+
+                // time exceeded
+                for (code, _) in time_exceeded_code_test_consts::VALID_VALUES {
+                    test_checksum_calc(TimeExceeded(code));
+                }
+
+                // parameter problem
+                for (code, _) in parameter_problem_code_test_consts::VALID_VALUES {
+                    test_checksum_calc(ParameterProblem(
+                        ParameterProblemHeader{
+                            code,
+                            pointer: u32::from_be_bytes(bytes5to8)
+                        }
+                    ));
+                }
+
+                // echo request
+                test_checksum_calc(EchoRequest(
+                    IcmpEchoHeader::from_bytes(bytes5to8)
+                ));
+
+                // echo reply
+                test_checksum_calc(EchoReply(
+                    IcmpEchoHeader::from_bytes(bytes5to8)
+                ));
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        #[cfg(not(target_pointer_width = "32"))]
+        fn to_header(
+            ip_header in ipv6_any(),
+            icmpv6_type in icmpv6_type_any(),
+            // max length is u32::MAX - header_len (7)
+            bad_len in (core::u32::MAX - 7) as usize..=(core::isize::MAX as usize),
+            payload in proptest::collection::vec(any::<u8>(), 0..1024)
+        ) {
+            // size error case
+            {
+                // SAFETY: In case the error is not triggered
+                //         a segmentation fault will be triggered.
+                let too_big_slice = unsafe {
+                    //NOTE: The pointer must be initialized with a non null value
+                    //      otherwise a key constraint of slices is not fulfilled
+                    //      which can lead to crashes in release mode.
+                    use core::ptr::NonNull;
+                    core::slice::from_raw_parts(
+                        NonNull::<u8>::dangling().as_ptr(),
+                        bad_len
+                    )
+                };
+                assert_eq!(
+                    icmpv6_type.to_header(ip_header.source, ip_header.destination, too_big_slice),
+                    Err(ValueTooBigError{
+                        actual: bad_len,
+                        max_allowed: (core::u32::MAX - 8) as usize,
+                        value_type: ValueType::Icmpv6PayloadLength,
+                    })
+                );
+            }
+            // normal case
+            assert_eq!(
+                icmpv6_type.to_header(ip_header.source, ip_header.destination, &payload).unwrap(),
+                Icmpv6Header {
+                    checksum: icmpv6_type.calc_checksum(ip_header.source, ip_header.destination, &payload).unwrap(),
+                    icmp_type: icmpv6_type,
+                }
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header_len(
+            code_u8 in any::<u8>(),
+            bytes5to8 in any::<[u8;4]>(),
+        ) {
+            let len_8_hdrs = [
+                DestinationUnreachable(DestUnreachableCode::Prohibited),
+                PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), },
+                TimeExceeded(TimeExceededCode::FragmentReassemblyTimeExceeded),
+                ParameterProblem(ParameterProblemHeader{
+                    code: ParameterProblemCode::UnrecognizedIpv6Option,
+                    pointer: u32::from_be_bytes(bytes5to8),
+                }),
+                EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)),
+                EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)),
+            ];
+
+            for hdr in len_8_hdrs {
+                assert_eq!(8, hdr.header_len());
+            }
+
+            for t in 0..=u8::MAX {
+                assert_eq!(
+                    8,
+                    Unknown{
+                        type_u8: t,
+                        code_u8,
+                        bytes5to8,
+                    }.header_len()
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn fixed_payload_size(
+            code_u8 in any::<u8>(),
+            bytes5to8 in any::<[u8;4]>(),
+        ) {
+            let variable_payload_headers = [
+                DestinationUnreachable(DestUnreachableCode::Prohibited),
+                PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), },
+                TimeExceeded(TimeExceededCode::HopLimitExceeded),
+                ParameterProblem(ParameterProblemHeader{
+                    code: ParameterProblemCode::SrUpperLayerHeaderError,
+                    pointer: u32::from_be_bytes(bytes5to8),
+                }),
+                EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)),
+                EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)),
+            ];
+
+            for hdr in variable_payload_headers {
+                assert_eq!(None, hdr.fixed_payload_size());
+            }
+
+            for t in 0..=u8::MAX {
+                assert_eq!(
+                    None,
+                    Unknown{
+                        type_u8: t,
+                        code_u8,
+                        bytes5to8,
+                    }.fixed_payload_size()
+                );
+            }
+        }
+    }
+
+    #[test]
+    fn debug() {
+        assert_eq!(
+            format!(
+                "{:?}",
+                Icmpv6Type::Unknown {
+                    type_u8: 0,
+                    code_u8: 1,
+                    bytes5to8: [2, 3, 4, 5]
+                }
+            ),
+            "Unknown { type_u8: 0, code_u8: 1, bytes5to8: [2, 3, 4, 5] }"
+        )
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(t in icmpv6_type_any()) {
+            assert_eq!(t, t.clone());
+        }
+    }
+}
diff --git a/src/transport/mod.rs b/src/transport/mod.rs
new file mode 100644
index 0000000..c09d57f
--- /dev/null
+++ b/src/transport/mod.rs
@@ -0,0 +1,25 @@
+pub mod icmp_echo_header;
+/// Module containing ICMPv4 related types and constants.
+pub mod icmpv4;
+pub mod icmpv4_header;
+pub mod icmpv4_slice;
+pub mod icmpv4_type;
+/// Module containing ICMPv6 related types and constants
+pub mod icmpv6;
+pub mod icmpv6_header;
+pub mod icmpv6_slice;
+pub mod icmpv6_type;
+pub mod tcp_header;
+pub mod tcp_header_slice;
+pub mod tcp_option_element;
+pub mod tcp_option_impl;
+pub mod tcp_option_read_error;
+pub mod tcp_option_write_error;
+pub mod tcp_options;
+pub mod tcp_options_iterator;
+pub mod tcp_slice;
+pub mod transport_header;
+pub mod transport_slice;
+pub mod udp_header;
+pub mod udp_header_slice;
+pub mod udp_slice;
diff --git a/src/transport/tcp_header.rs b/src/transport/tcp_header.rs
new file mode 100644
index 0000000..a26ce53
--- /dev/null
+++ b/src/transport/tcp_header.rs
@@ -0,0 +1,1888 @@
+use arrayvec::ArrayVec;
+
+use crate::err::{ValueTooBigError, ValueType};
+
+use super::super::*;
+
+/// Deprecated use [`TcpHeader::MIN_LEN`] instead.
+#[deprecated(since = "0.14.0", note = "Use `TcpHeader::MIN_LEN` instead")]
+pub const TCP_MINIMUM_HEADER_SIZE: usize = 5 * 4;
+
+/// Deprecated use [`TcpHeader::MIN_DATA_OFFSET`] instead.
+#[deprecated(since = "0.14.0", note = "Use `TcpHeader::MIN_DATA_OFFSET` instead")]
+pub const TCP_MINIMUM_DATA_OFFSET: u8 = 5;
+
+/// Deprecated use [`TcpHeader::MAX_DATA_OFFSET`] instead.
+#[deprecated(since = "0.14.0", note = "Use `TcpHeader::MAX_DATA_OFFSET` instead")]
+pub const TCP_MAXIMUM_DATA_OFFSET: u8 = 0xf;
+
+/// TCP header according to rfc 793.
+///
+/// Field descriptions copied from RFC 793 page 15++
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub struct TcpHeader {
+    /// The source port number.
+    pub source_port: u16,
+    /// The destination port number.
+    pub destination_port: u16,
+    /// The sequence number of the first data octet in this segment (except when SYN is present).
+    ///
+    /// If SYN is present the sequence number is the initial sequence number (ISN)
+    /// and the first data octet is ISN+1.
+    /// [copied from RFC 793, page 16]
+    pub sequence_number: u32,
+    /// If the ACK control bit is set this field contains the value of the
+    /// next sequence number the sender of the segment is expecting to
+    /// receive.
+    ///
+    /// Once a connection is established this is always sent.
+    pub acknowledgment_number: u32,
+    /// ECN-nonce - concealment protection (experimental: see RFC 3540)
+    pub ns: bool,
+    /// No more data from sender
+    pub fin: bool,
+    /// Synchronize sequence numbers
+    pub syn: bool,
+    /// Reset the connection
+    pub rst: bool,
+    /// Push Function
+    pub psh: bool,
+    /// Acknowledgment field significant
+    pub ack: bool,
+    /// Urgent Pointer field significant
+    pub urg: bool,
+    /// ECN-Echo (RFC 3168)
+    pub ece: bool,
+    /// Congestion Window Reduced (CWR) flag
+    ///
+    /// This flag is set by the sending host to indicate that it received a TCP segment with the ECE flag set and had responded in congestion control mechanism (added to header by RFC 3168).
+    pub cwr: bool,
+    /// The number of data octets beginning with the one indicated in the
+    /// acknowledgment field which the sender of this segment is willing to
+    /// accept.
+    pub window_size: u16,
+    /// Checksum (16 bit one's complement) of the pseudo ip header, this tcp header and the payload.
+    pub checksum: u16,
+    /// This field communicates the current value of the urgent pointer as a
+    /// positive offset from the sequence number in this segment.
+    ///
+    /// The urgent pointer points to the sequence number of the octet following
+    /// the urgent data.  This field is only be interpreted in segments with
+    /// the URG control bit set.
+    pub urgent_pointer: u16,
+
+    /// Options in the TCP header.
+    pub options: TcpOptions,
+}
+
+impl TcpHeader {
+    /// Minimum length of a TCP header in bytes/octets.
+    pub const MIN_LEN: usize = 5 * 4;
+
+    /// Maximum length of a TCP header in bytes/octets.
+    ///
+    /// The length is obtained by multiplying the maximum value
+    /// that "data offset" can take (it is a 4 bit number so the max
+    /// is 0b1111) and multiplying it by 4 as it describes the offset
+    /// to the data in 4-bytes words.
+    pub const MAX_LEN: usize = 0b1111 * 4;
+
+    /// The minimum data offset size (size of the tcp header itself).
+    pub const MIN_DATA_OFFSET: u8 = 5;
+
+    /// The maximum allowed value for the data offset (it is a 4 bit value).
+    pub const MAX_DATA_OFFSET: u8 = 0xf;
+
+    /// Creates a TcpHeader with the given values and the rest initialized with default values.
+    pub fn new(
+        source_port: u16,
+        destination_port: u16,
+        sequence_number: u32,
+        window_size: u16,
+    ) -> TcpHeader {
+        TcpHeader {
+            source_port,
+            destination_port,
+            sequence_number,
+            acknowledgment_number: 0,
+            ns: false,
+            fin: false,
+            syn: false,
+            rst: false,
+            psh: false,
+            ack: false,
+            ece: false,
+            urg: false,
+            cwr: false,
+            window_size,
+            checksum: 0,
+            urgent_pointer: 0,
+            options: Default::default(),
+        }
+    }
+
+    /// The number of 32 bit words in the TCP Header & TCP header options.
+    ///
+    /// This indicates where the data begins relative to the start of an
+    /// TCP header in multiples of 4 bytes. This number is
+    /// present in the `data_offset` field of the header and defines
+    /// the length of the tcp options present.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use etherparse::{TcpHeader, TcpOptions};
+    ///
+    /// {
+    ///     let header = TcpHeader{
+    ///         options: TcpOptions::try_from_slice(&[]).unwrap(),
+    ///         .. Default::default()
+    ///     };
+    ///     // in case there are no options the minimum size of the tcp
+    ///     // is returned.
+    ///     assert_eq!(5, header.data_offset());
+    /// }
+    /// {
+    ///     let header = TcpHeader{
+    ///         options: TcpOptions::try_from_slice(&[1,2,3,4,5,6,7,8]).unwrap(),
+    ///         .. Default::default()
+    ///     };
+    ///     // otherwise the base TCP header size plus the number of 4 byte
+    ///     // words in the options is returned
+    ///     assert_eq!(5 + 2, header.data_offset());
+    /// }
+    /// ```
+    #[inline]
+    pub fn data_offset(&self) -> u8 {
+        self.options.data_offset()
+    }
+
+    /// Returns the length of the header including the options.
+    #[inline]
+    pub fn header_len(&self) -> usize {
+        20 + self.options.len()
+    }
+
+    /// Returns the length of the header including the options.
+    #[inline]
+    pub fn header_len_u16(&self) -> u16 {
+        20 + u16::from(self.options.len_u8())
+    }
+
+    /// Returns the options size in bytes based on the currently set data_offset. Returns None if the data_offset is smaller then the minimum size or bigger then the maximum supported size.
+    #[inline]
+    #[deprecated(since = "0.14.0", note = "Please use `options.len()` instead")]
+    pub fn options_len(&self) -> usize {
+        self.options.len()
+    }
+
+    /// Returns a slice containing the options of the header (size is determined via the data_offset field.
+    #[inline]
+    #[deprecated(since = "0.14.0", note = "Please use `options.as_slice()` instead")]
+    pub fn options(&self) -> &[u8] {
+        self.options.as_slice()
+    }
+
+    /// Sets the options (overwrites the current options) or returns
+    /// an error when there is not enough space.
+    pub fn set_options(
+        &mut self,
+        elements: &[TcpOptionElement],
+    ) -> Result<(), TcpOptionWriteError> {
+        self.options = TcpOptions::try_from_elements(elements)?;
+        Ok(())
+    }
+
+    /// Sets the options to the data given.
+    pub fn set_options_raw(&mut self, data: &[u8]) -> Result<(), TcpOptionWriteError> {
+        self.options = TcpOptions::try_from_slice(data)?;
+        Ok(())
+    }
+
+    /// Returns an iterator that allows to iterate through all
+    /// known TCP header options.
+    #[inline]
+    pub fn options_iterator(&self) -> TcpOptionsIterator {
+        self.options.elements_iter()
+    }
+
+    /// Renamed to `TcpHeader::from_slice`
+    #[deprecated(since = "0.10.1", note = "Use TcpHeader::from_slice instead.")]
+    #[inline]
+    pub fn read_from_slice(slice: &[u8]) -> Result<(TcpHeader, &[u8]), err::tcp::HeaderSliceError> {
+        TcpHeader::from_slice(slice)
+    }
+
+    /// Reads a tcp header from a slice
+    #[inline]
+    pub fn from_slice(slice: &[u8]) -> Result<(TcpHeader, &[u8]), err::tcp::HeaderSliceError> {
+        let h = TcpHeaderSlice::from_slice(slice)?;
+        Ok((h.to_header(), &slice[h.slice().len()..]))
+    }
+
+    /// Read a tcp header from the current position
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read<T: std::io::Read + Sized>(
+        reader: &mut T,
+    ) -> Result<TcpHeader, err::tcp::HeaderReadError> {
+        use err::tcp::{HeaderError::*, HeaderReadError::*};
+
+        let raw = {
+            let mut raw: [u8; 20] = [0; 20];
+            reader.read_exact(&mut raw).map_err(Io)?;
+            raw
+        };
+        let source_port = u16::from_be_bytes([raw[0], raw[1]]);
+        let destination_port = u16::from_be_bytes([raw[2], raw[3]]);
+        let sequence_number = u32::from_be_bytes([raw[4], raw[5], raw[6], raw[7]]);
+        let acknowledgment_number = u32::from_be_bytes([raw[8], raw[9], raw[10], raw[11]]);
+        let (data_offset, ns) = {
+            let value = raw[12];
+            ((value & 0xf0) >> 4, 0 != value & 1)
+        };
+        let flags = raw[13];
+
+        Ok(TcpHeader {
+            source_port,
+            destination_port,
+            sequence_number,
+            acknowledgment_number,
+            ns,
+            fin: 0 != flags & 1,
+            syn: 0 != flags & 2,
+            rst: 0 != flags & 4,
+            psh: 0 != flags & 8,
+            ack: 0 != flags & 16,
+            urg: 0 != flags & 32,
+            ece: 0 != flags & 64,
+            cwr: 0 != flags & 128,
+            window_size: u16::from_be_bytes([raw[14], raw[15]]),
+            checksum: u16::from_be_bytes([raw[16], raw[17]]),
+            urgent_pointer: u16::from_be_bytes([raw[18], raw[19]]),
+            options: {
+                if data_offset < TcpHeader::MIN_DATA_OFFSET {
+                    return Err(Content(DataOffsetTooSmall { data_offset }));
+                } else {
+                    let mut options = TcpOptions {
+                        len: (data_offset - TcpHeader::MIN_DATA_OFFSET) << 2,
+                        buf: [0; 40],
+                    };
+                    // convert to bytes minus the tcp header size itself
+                    if options.len > 0 {
+                        reader
+                            .read_exact(&mut options.buf[..options.len.into()])
+                            .map_err(Io)?;
+                    }
+                    options
+                }
+            },
+        })
+    }
+
+    /// Write the tcp header to a stream (does NOT calculate the checksum).
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
+        //check that the data offset is within range
+        let src_be = self.source_port.to_be_bytes();
+        let dst_be = self.destination_port.to_be_bytes();
+        let seq_be = self.sequence_number.to_be_bytes();
+        let ack_be = self.acknowledgment_number.to_be_bytes();
+        let window_be = self.window_size.to_be_bytes();
+        let checksum_be = self.checksum.to_be_bytes();
+        let urg_ptr_be = self.urgent_pointer.to_be_bytes();
+        let data_offset = self.data_offset();
+        debug_assert!(TcpHeader::MIN_DATA_OFFSET <= data_offset);
+        debug_assert!(data_offset <= TcpHeader::MAX_DATA_OFFSET);
+
+        writer.write_all(&[
+            src_be[0],
+            src_be[1],
+            dst_be[0],
+            dst_be[1],
+            seq_be[0],
+            seq_be[1],
+            seq_be[2],
+            seq_be[3],
+            ack_be[0],
+            ack_be[1],
+            ack_be[2],
+            ack_be[3],
+            {
+                let value = (data_offset << 4) & 0xF0;
+                if self.ns {
+                    value | 1
+                } else {
+                    value
+                }
+            },
+            {
+                let mut value = 0;
+                if self.fin {
+                    value |= 1;
+                }
+                if self.syn {
+                    value |= 2;
+                }
+                if self.rst {
+                    value |= 4;
+                }
+                if self.psh {
+                    value |= 8;
+                }
+                if self.ack {
+                    value |= 16;
+                }
+                if self.urg {
+                    value |= 32;
+                }
+                if self.ece {
+                    value |= 64;
+                }
+                if self.cwr {
+                    value |= 128;
+                }
+                value
+            },
+            window_be[0],
+            window_be[1],
+            checksum_be[0],
+            checksum_be[1],
+            urg_ptr_be[0],
+            urg_ptr_be[1],
+        ])?;
+
+        // write options if the data_offset is large enough
+        let options = self.options.as_slice();
+        if false == options.is_empty() {
+            writer.write_all(options)?;
+        }
+        Ok(())
+    }
+
+    /// Returns the serialized header.
+    pub fn to_bytes(&self) -> ArrayVec<u8, { TcpHeader::MAX_LEN }> {
+        //check that the data offset is within range
+        let src_be = self.source_port.to_be_bytes();
+        let dst_be = self.destination_port.to_be_bytes();
+        let seq_be = self.sequence_number.to_be_bytes();
+        let ack_be = self.acknowledgment_number.to_be_bytes();
+        let window_be = self.window_size.to_be_bytes();
+        let checksum_be = self.checksum.to_be_bytes();
+        let urg_ptr_be = self.urgent_pointer.to_be_bytes();
+
+        let mut result = ArrayVec::new();
+
+        // write base header data
+        result.extend([
+            src_be[0],
+            src_be[1],
+            dst_be[0],
+            dst_be[1],
+            seq_be[0],
+            seq_be[1],
+            seq_be[2],
+            seq_be[3],
+            ack_be[0],
+            ack_be[1],
+            ack_be[2],
+            ack_be[3],
+            {
+                let value = (self.data_offset() << 4) & 0xF0;
+                if self.ns {
+                    value | 1
+                } else {
+                    value
+                }
+            },
+            {
+                let mut value = 0;
+                if self.fin {
+                    value |= 1;
+                }
+                if self.syn {
+                    value |= 2;
+                }
+                if self.rst {
+                    value |= 4;
+                }
+                if self.psh {
+                    value |= 8;
+                }
+                if self.ack {
+                    value |= 16;
+                }
+                if self.urg {
+                    value |= 32;
+                }
+                if self.ece {
+                    value |= 64;
+                }
+                if self.cwr {
+                    value |= 128;
+                }
+                value
+            },
+            window_be[0],
+            window_be[1],
+            checksum_be[0],
+            checksum_be[1],
+            urg_ptr_be[0],
+            urg_ptr_be[1],
+        ]);
+
+        // add the options
+        result.extend(self.options.buf);
+        // SAFETY: Safe as the header len can not exceed the maximum length
+        // of the header.
+        unsafe {
+            result.set_len(self.header_len());
+        }
+
+        result
+    }
+
+    /// Calculates the upd header checksum based on a ipv4 header and returns the result. This does NOT set the checksum.
+    pub fn calc_checksum_ipv4(
+        &self,
+        ip_header: &Ipv4Header,
+        payload: &[u8],
+    ) -> Result<u16, ValueTooBigError<usize>> {
+        self.calc_checksum_ipv4_raw(ip_header.source, ip_header.destination, payload)
+    }
+
+    /// Calculates the checksum for the current header in ipv4 mode and returns the result. This does NOT set the checksum.
+    pub fn calc_checksum_ipv4_raw(
+        &self,
+        source_ip: [u8; 4],
+        destination_ip: [u8; 4],
+        payload: &[u8],
+    ) -> Result<u16, ValueTooBigError<usize>> {
+        // check that the total length fits into the tcp length field
+        let max_payload = usize::from(u16::MAX) - self.header_len();
+        if max_payload < payload.len() {
+            return Err(ValueTooBigError {
+                actual: payload.len(),
+                max_allowed: max_payload,
+                value_type: ValueType::TcpPayloadLengthIpv4,
+            });
+        }
+
+        // calculate the checksum
+        let tcp_len = self.header_len_u16() + (payload.len() as u16);
+        Ok(self.calc_checksum_post_ip(
+            checksum::Sum16BitWords::new()
+                .add_4bytes(source_ip)
+                .add_4bytes(destination_ip)
+                .add_2bytes([0, ip_number::TCP.0])
+                .add_2bytes(tcp_len.to_be_bytes()),
+            payload,
+        ))
+    }
+
+    /// Calculates the upd header checksum based on a ipv6 header and returns the result. This does NOT set the checksum..
+    pub fn calc_checksum_ipv6(
+        &self,
+        ip_header: &Ipv6Header,
+        payload: &[u8],
+    ) -> Result<u16, ValueTooBigError<usize>> {
+        self.calc_checksum_ipv6_raw(ip_header.source, ip_header.destination, payload)
+    }
+
+    /// Calculates the checksum for the current header in ipv6 mode and returns the result. This does NOT set the checksum.
+    pub fn calc_checksum_ipv6_raw(
+        &self,
+        source: [u8; 16],
+        destination: [u8; 16],
+        payload: &[u8],
+    ) -> Result<u16, ValueTooBigError<usize>> {
+        // check that the total length fits into the tcp length field
+        let max_payload = (u32::MAX as usize) - self.header_len();
+        if max_payload < payload.len() {
+            return Err(ValueTooBigError {
+                actual: payload.len(),
+                max_allowed: max_payload,
+                value_type: ValueType::TcpPayloadLengthIpv6,
+            });
+        }
+
+        let tcp_len = u32::from(self.header_len_u16()) + (payload.len() as u32);
+        Ok(self.calc_checksum_post_ip(
+            checksum::Sum16BitWords::new()
+                .add_16bytes(source)
+                .add_16bytes(destination)
+                .add_4bytes(tcp_len.to_be_bytes())
+                .add_2bytes([0, ip_number::TCP.0]),
+            payload,
+        ))
+    }
+
+    ///This method takes the sum of the pseudo ip header and calculates the rest of the checksum.
+    fn calc_checksum_post_ip(
+        &self,
+        ip_pseudo_header_sum: checksum::Sum16BitWords,
+        payload: &[u8],
+    ) -> u16 {
+        ip_pseudo_header_sum
+            .add_2bytes(self.source_port.to_be_bytes())
+            .add_2bytes(self.destination_port.to_be_bytes())
+            .add_4bytes(self.sequence_number.to_be_bytes())
+            .add_4bytes(self.acknowledgment_number.to_be_bytes())
+            .add_2bytes([
+                {
+                    let value = (self.data_offset() << 4) & 0xF0;
+                    if self.ns {
+                        value | 1
+                    } else {
+                        value
+                    }
+                },
+                {
+                    let mut value = 0;
+                    if self.fin {
+                        value |= 1;
+                    }
+                    if self.syn {
+                        value |= 2;
+                    }
+                    if self.rst {
+                        value |= 4;
+                    }
+                    if self.psh {
+                        value |= 8;
+                    }
+                    if self.ack {
+                        value |= 16;
+                    }
+                    if self.urg {
+                        value |= 32;
+                    }
+                    if self.ece {
+                        value |= 64;
+                    }
+                    if self.cwr {
+                        value |= 128;
+                    }
+                    value
+                },
+            ])
+            .add_2bytes(self.window_size.to_be_bytes())
+            .add_2bytes(self.urgent_pointer.to_be_bytes())
+            .add_slice(self.options.as_slice())
+            .add_slice(payload)
+            .ones_complement()
+            .to_be()
+    }
+}
+
+impl Default for TcpHeader {
+    fn default() -> TcpHeader {
+        TcpHeader {
+            source_port: 0,
+            destination_port: 0,
+            sequence_number: 0,
+            acknowledgment_number: 0,
+            ns: false,
+            fin: false,
+            syn: false,
+            rst: false,
+            psh: false,
+            ack: false,
+            urg: false,
+            ece: false,
+            cwr: false,
+            window_size: 0,
+            checksum: 0,
+            urgent_pointer: 0,
+            options: TcpOptions {
+                len: 0,
+                buf: [0u8; 40],
+            },
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{
+        err::{
+            tcp::{HeaderError::*, HeaderSliceError::*},
+            ValueTooBigError, ValueType,
+        },
+        tcp_option::*,
+        test_gens::*,
+        TcpOptionElement::*,
+        *,
+    };
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+    use std::io::Cursor;
+
+    #[test]
+    fn default() {
+        let default: TcpHeader = Default::default();
+
+        assert_eq!(0, default.source_port);
+        assert_eq!(0, default.destination_port);
+        assert_eq!(0, default.sequence_number);
+        assert_eq!(0, default.acknowledgment_number);
+        assert_eq!(5, default.data_offset());
+        assert_eq!(false, default.ns);
+        assert_eq!(false, default.fin);
+        assert_eq!(false, default.syn);
+        assert_eq!(false, default.rst);
+        assert_eq!(false, default.psh);
+        assert_eq!(false, default.ack);
+        assert_eq!(false, default.ece);
+        assert_eq!(false, default.urg);
+        assert_eq!(false, default.cwr);
+        assert_eq!(0, default.window_size);
+        assert_eq!(0, default.checksum);
+        assert_eq!(0, default.urgent_pointer);
+        assert_eq!(0, default.options.as_slice().len());
+    }
+
+    proptest! {
+        #[test]
+        fn debug(header in tcp_any()) {
+
+            // normal debug printing
+            assert_eq!(
+                format!(
+                    "TcpHeader {{ source_port: {}, destination_port: {}, sequence_number: {}, acknowledgment_number: {}, ns: {}, fin: {}, syn: {}, rst: {}, psh: {}, ack: {}, urg: {}, ece: {}, cwr: {}, window_size: {}, checksum: {}, urgent_pointer: {}, options: {:?} }}",
+                    header.source_port,
+                    header.destination_port,
+                    header.sequence_number,
+                    header.acknowledgment_number,
+                    header.ns,
+                    header.fin,
+                    header.syn,
+                    header.rst,
+                    header.psh,
+                    header.ack,
+                    header.urg,
+                    header.ece,
+                    header.cwr,
+                    header.window_size,
+                    header.checksum,
+                    header.urgent_pointer,
+                    header.options_iterator()
+                ),
+                format!("{:?}", header)
+            );
+
+            // multi line debug printing
+            {
+                let mut header = header.clone();
+                // lets exclude options for now, as I am not quiet sure
+                // how to introduce additional indentation and the options
+                // part is already checked by the previous test
+                header.set_options(&[]).unwrap();
+                assert_eq!(
+                    format!(
+                        "TcpHeader {{
+    source_port: {},
+    destination_port: {},
+    sequence_number: {},
+    acknowledgment_number: {},
+    ns: {},
+    fin: {},
+    syn: {},
+    rst: {},
+    psh: {},
+    ack: {},
+    urg: {},
+    ece: {},
+    cwr: {},
+    window_size: {},
+    checksum: {},
+    urgent_pointer: {},
+    options: {:?},
+}}",
+                        header.source_port,
+                        header.destination_port,
+                        header.sequence_number,
+                        header.acknowledgment_number,
+                        header.ns,
+                        header.fin,
+                        header.syn,
+                        header.rst,
+                        header.psh,
+                        header.ack,
+                        header.urg,
+                        header.ece,
+                        header.cwr,
+                        header.window_size,
+                        header.checksum,
+                        header.urgent_pointer,
+                        header.options_iterator()
+                    ),
+                    format!("{:#?}", header)
+                );
+            }
+        }
+    }
+
+    #[test]
+    fn eq() {
+        let options = [
+            TcpOptionElement::Timestamp(0x00102030, 0x01112131), //10
+            TcpOptionElement::SelectiveAcknowledgement(
+                (0x02122232, 0x03132333),
+                [None, None, None],
+            ), //20
+            TcpOptionElement::Timestamp(0x04142434, 0x05152535), //30
+            TcpOptionElement::Timestamp(0x06162636, 0x07172737), //40
+        ];
+
+        let base: TcpHeader = {
+            let mut base: TcpHeader = Default::default();
+            base.source_port = 1;
+            base.destination_port = 2;
+            base.sequence_number = 3;
+            base.acknowledgment_number = 4;
+            base.window_size = 6;
+            base.checksum = 7;
+            base.urgent_pointer = 8;
+            base.set_options(&options[..]).unwrap();
+
+            base
+        };
+
+        //equal
+        {
+            let other = base.clone();
+            assert_eq!(other, base);
+        }
+        //change every field anc check for neq
+        //source_port
+        {
+            let mut other = base.clone();
+            other.source_port = 10;
+            assert_ne!(other, base);
+        }
+        //destination_port
+        {
+            let mut other = base.clone();
+            other.destination_port = 10;
+            assert_ne!(other, base);
+        }
+        //sequence_number
+        {
+            let mut other = base.clone();
+            other.sequence_number = 10;
+            assert_ne!(other, base);
+        }
+        //acknowledgment_number
+        {
+            let mut other = base.clone();
+            other.acknowledgment_number = 10;
+            assert_ne!(other, base);
+        }
+        //data_offset
+        {
+            let mut other = base.clone();
+            other
+                .set_options(&[TcpOptionElement::MaximumSegmentSize(16)])
+                .unwrap();
+            assert_ne!(other, base);
+        }
+        //ns
+        {
+            let mut other = base.clone();
+            other.ns = true;
+            assert_ne!(other, base);
+        }
+        //fin
+        {
+            let mut other = base.clone();
+            other.fin = true;
+            assert_ne!(other, base);
+        }
+        //syn
+        {
+            let mut other = base.clone();
+            other.syn = true;
+            assert_ne!(other, base);
+        }
+        //rst
+        {
+            let mut other = base.clone();
+            other.rst = true;
+            assert_ne!(other, base);
+        }
+        //psh
+        {
+            let mut other = base.clone();
+            other.psh = true;
+            assert_ne!(other, base);
+        }
+        //ack
+        {
+            let mut other = base.clone();
+            other.ack = true;
+            assert_ne!(other, base);
+        }
+        //ece
+        {
+            let mut other = base.clone();
+            other.ece = true;
+            assert_ne!(other, base);
+        }
+        //urg
+        {
+            let mut other = base.clone();
+            other.urg = true;
+            assert_ne!(other, base);
+        }
+        //cwr
+        {
+            let mut other = base.clone();
+            other.cwr = true;
+            assert_ne!(other, base);
+        }
+        //window_size
+        {
+            let mut other = base.clone();
+            other.window_size = 10;
+            assert_ne!(other, base);
+        }
+        //checksum
+        {
+            let mut other = base.clone();
+            other.checksum = 10;
+            assert_ne!(other, base);
+        }
+        //urgent_pointer
+        {
+            let mut other = base.clone();
+            other.urgent_pointer = 10;
+            assert_ne!(other, base);
+        }
+        //options (first element different)
+        {
+            let mut other = base.clone();
+            other
+                .set_options(&{
+                    let mut other_options = options.clone();
+                    other_options[0] = TcpOptionElement::Timestamp(0x00102039, 0x01112131);
+                    other_options
+                })
+                .unwrap();
+
+            assert_ne!(other, base);
+        }
+        //options (last element)
+        {
+            let mut other = base.clone();
+            other.set_options(&options).unwrap();
+
+            let mut other2 = base.clone();
+            other2
+                .set_options(&{
+                    let mut options2 = options.clone();
+                    options2[3] = TcpOptionElement::Timestamp(0x06162636, 0x97172737);
+                    options2
+                })
+                .unwrap();
+
+            assert_ne!(other, other2);
+        }
+        //options (check only relevant data is compared)
+        {
+            let mut other = base.clone();
+            other.set_options(&options).unwrap();
+
+            let mut other2 = base.clone();
+            other2
+                .set_options(&{
+                    let mut options2 = options.clone();
+                    options2[3] = TcpOptionElement::Timestamp(0x06162636, 0x97172737);
+                    options2
+                })
+                .unwrap();
+
+            // reset the data
+            let new_options = [TcpOptionElement::Timestamp(0x00102030, 0x01112131)];
+            other.set_options(&new_options).unwrap();
+            other2.set_options(&new_options).unwrap();
+
+            assert_eq!(other, other2);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn hash(header in tcp_any()) {
+            use std::collections::hash_map::DefaultHasher;
+            use core::hash::{Hash, Hasher};
+            let a = {
+                let mut hasher = DefaultHasher::new();
+                header.hash(&mut hasher);
+                hasher.finish()
+            };
+            let b = {
+                let mut hasher = DefaultHasher::new();
+                header.hash(&mut hasher);
+                hasher.finish()
+            };
+            assert_eq!(a, b);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn new(
+            source_port in any::<u16>(),
+            destination_port in any::<u16>(),
+            sequence_number in any::<u32>(),
+            window_size in any::<u16>()
+        ) {
+            let header = TcpHeader::new(
+                source_port,
+                destination_port,
+                sequence_number,
+                window_size
+            );
+            assert_eq!(header.source_port, source_port);
+            assert_eq!(header.destination_port, destination_port);
+            assert_eq!(header.sequence_number, sequence_number);
+            assert_eq!(header.acknowledgment_number, 0);
+            assert_eq!(header.ns, false);
+            assert_eq!(header.fin, false);
+            assert_eq!(header.syn, false);
+            assert_eq!(header.rst, false);
+            assert_eq!(header.psh, false);
+            assert_eq!(header.ack, false);
+            assert_eq!(header.urg, false);
+            assert_eq!(header.ece, false);
+            assert_eq!(header.cwr, false);
+            assert_eq!(header.window_size, window_size);
+            assert_eq!(header.checksum, 0);
+            assert_eq!(header.urgent_pointer, 0);
+            assert_eq!(header.options.as_slice(), &[]);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn data_offset(header in tcp_any()) {
+            assert_eq!(header.options.len()/4 + 5, header.data_offset().into());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header_len(header in tcp_any()) {
+            assert_eq!(
+                header.header_len(),
+                (20 + header.options.len())
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn header_len_u16(header in tcp_any()) {
+            assert_eq!(
+                header.header_len_u16(),
+                (20 + header.options.len()) as u16
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        #[allow(deprecated)]
+        fn options_len(header in tcp_any()) {
+            assert_eq!(
+                header.options_len(),
+                header.to_bytes().len() - 20
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        #[allow(deprecated)]
+        fn options(header in tcp_any()) {
+            assert_eq!(
+                header.options(),
+                &header.to_bytes()[20..]
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        #[rustfmt::skip]
+        fn set_options(
+            header in tcp_any(),
+            arg_u8 in any::<u8>(),
+            arg_u16 in any::<u16>(),
+            ack_args in proptest::collection::vec(any::<u32>(), 4*2),
+            arg0_u32 in any::<u32>(),
+            arg1_u32 in any::<u32>()
+        ) {
+            use crate::TcpOptionElement::*;
+
+            // maximum segment size
+            {
+                let mut header = header.clone();
+                header.set_options(
+                    &[Noop, Noop, MaximumSegmentSize(arg_u16), Noop]
+                ).unwrap();
+                assert_eq!(
+                    header.options.as_slice(),
+                    &{
+                        let arg_be = arg_u16.to_be_bytes();
+                        [
+                            KIND_NOOP, KIND_NOOP, KIND_MAXIMUM_SEGMENT_SIZE, 4,
+                            arg_be[0], arg_be[1], KIND_NOOP, KIND_END
+                        ]
+                    }
+                );
+            }
+
+            // window scale
+            {
+                let mut header = header.clone();
+                header.set_options(
+                    &[Noop, Noop, WindowScale(arg_u8), Noop]
+                ).unwrap();
+                assert_eq!(
+                    header.options.as_slice(),
+                    &[
+                        KIND_NOOP, KIND_NOOP, KIND_WINDOW_SCALE, 3,
+                        arg_u8, KIND_NOOP, KIND_END, 0
+                    ]
+                );
+            }
+
+            // selective ack permitted
+            {
+                let mut header = header.clone();
+                header.set_options(
+                    &[Noop, Noop, SelectiveAcknowledgementPermitted, Noop]
+                ).unwrap();
+                assert_eq!(
+                    header.options.as_slice(),
+                    &[
+                        KIND_NOOP, KIND_NOOP, KIND_SELECTIVE_ACK_PERMITTED, 2,
+                        KIND_NOOP, KIND_END, 0, 0
+                    ]
+                );
+            }
+
+            // selective ack
+            {
+                let args_be : Vec<[u8;4]> = ack_args.iter().map(|v| v.to_be_bytes()).collect();
+
+                //1
+                {
+                    let mut header = header.clone();
+                    header.set_options(
+                        &[Noop, Noop, SelectiveAcknowledgement((ack_args[0], ack_args[1]), [None, None, None]), Noop]
+                    ).unwrap();
+                    assert_eq!(
+                        header.options.as_slice(),
+                        &[
+                            KIND_NOOP, KIND_NOOP, KIND_SELECTIVE_ACK, 10,
+                            args_be[0][0], args_be[0][1], args_be[0][2], args_be[0][3],
+                            args_be[1][0], args_be[1][1], args_be[1][2], args_be[1][3],
+                            KIND_NOOP, KIND_END, 0, 0
+                        ]
+                    );
+                }
+
+                //2
+                {
+                    let mut header = header.clone();
+                    header.set_options(
+                        &[
+                            Noop,
+                            Noop,
+                            SelectiveAcknowledgement(
+                                (ack_args[0], ack_args[1]),
+                                [Some((ack_args[2], ack_args[3])), None, None]
+                            ),
+                            Noop
+                        ]
+                    ).unwrap();
+                    assert_eq!(
+                        header.options.as_slice(),
+                        [
+                            KIND_NOOP, KIND_NOOP, KIND_SELECTIVE_ACK, 18,
+                            args_be[0][0], args_be[0][1], args_be[0][2], args_be[0][3],
+                            args_be[1][0], args_be[1][1], args_be[1][2], args_be[1][3],
+                            args_be[2][0], args_be[2][1], args_be[2][2], args_be[2][3],
+                            args_be[3][0], args_be[3][1], args_be[3][2], args_be[3][3],
+                            KIND_NOOP, KIND_END, 0, 0
+                        ]
+                    );
+                }
+
+                //3
+                {
+                    let mut header = header.clone();
+                    header.set_options(
+                        &[
+                            Noop,
+                            Noop,
+                            SelectiveAcknowledgement(
+                                (ack_args[0], ack_args[1]),
+                                [
+                                    Some((ack_args[2], ack_args[3])),
+                                    Some((ack_args[4], ack_args[5])),
+                                    None
+                                ]
+                            ),
+                            Noop
+                        ]
+                    ).unwrap();
+                    assert_eq!(
+                        header.options.as_slice(),
+                        &[
+                            KIND_NOOP, KIND_NOOP, KIND_SELECTIVE_ACK, 26,
+                            args_be[0][0], args_be[0][1], args_be[0][2], args_be[0][3],
+                            args_be[1][0], args_be[1][1], args_be[1][2], args_be[1][3],
+                            args_be[2][0], args_be[2][1], args_be[2][2], args_be[2][3],
+                            args_be[3][0], args_be[3][1], args_be[3][2], args_be[3][3],
+                            args_be[4][0], args_be[4][1], args_be[4][2], args_be[4][3],
+                            args_be[5][0], args_be[5][1], args_be[5][2], args_be[5][3],
+                            KIND_NOOP, KIND_END, 0, 0
+                        ]
+                    );
+                }
+
+                //4
+                {
+                    let mut header = header.clone();
+                    header.set_options(
+                        &[
+                            Noop,
+                            Noop,
+                            SelectiveAcknowledgement(
+                                (ack_args[0], ack_args[1]),
+                                [
+                                    Some((ack_args[2], ack_args[3])),
+                                    Some((ack_args[4], ack_args[5])),
+                                    Some((ack_args[6], ack_args[7]))
+                                ]
+                            ),
+                            Noop
+                        ]
+                    ).unwrap();
+                    assert_eq!(
+                        header.options.as_slice(),
+                        &[
+                            KIND_NOOP, KIND_NOOP, KIND_SELECTIVE_ACK, 34,
+                            args_be[0][0], args_be[0][1], args_be[0][2], args_be[0][3],
+                            args_be[1][0], args_be[1][1], args_be[1][2], args_be[1][3],
+                            args_be[2][0], args_be[2][1], args_be[2][2], args_be[2][3],
+                            args_be[3][0], args_be[3][1], args_be[3][2], args_be[3][3],
+                            args_be[4][0], args_be[4][1], args_be[4][2], args_be[4][3],
+                            args_be[5][0], args_be[5][1], args_be[5][2], args_be[5][3],
+                            args_be[6][0], args_be[6][1], args_be[6][2], args_be[6][3],
+                            args_be[7][0], args_be[7][1], args_be[7][2], args_be[7][3],
+                            KIND_NOOP, KIND_END, 0, 0
+                        ]
+                    );
+                }
+            }
+
+            // timestamp
+            {
+                let mut header = header.clone();
+                header.set_options(
+                    &[Noop, Noop, Timestamp(arg0_u32, arg1_u32), Noop]
+                ).unwrap();
+                assert_eq!(
+                    header.options.as_slice(),
+                    &{
+                        let arg0_be = arg0_u32.to_be_bytes();
+                        let arg1_be = arg1_u32.to_be_bytes();
+                        [
+                            KIND_NOOP, KIND_NOOP, KIND_TIMESTAMP, 10,
+                            arg0_be[0], arg0_be[1], arg0_be[2], arg0_be[3],
+                            arg1_be[0], arg1_be[1], arg1_be[2], arg1_be[3],
+                            KIND_NOOP, KIND_END, 0, 0
+                        ]
+                    }
+                );
+            }
+
+            // check for padding
+            {
+                let mut header = header.clone();
+                header.set_options(&[
+                    MaximumSegmentSize(1400),          // 4
+                    SelectiveAcknowledgementPermitted, // 2
+                    Timestamp(2661445915, 0),          // 10
+                    Noop,                              // 1
+                    WindowScale(7),                    // 3
+                ]).unwrap(); // total 20
+                // + header 20 = 40 byte
+                assert_eq!(40, header.header_len());
+            }
+
+            // not enough memory error
+            {
+                let mut header = header.clone();
+                assert_eq!(
+                    Err(TcpOptionWriteError::NotEnoughSpace(41)),
+                    header.set_options(&[
+                        MaximumSegmentSize(1),                                        //4
+                        WindowScale(2),                                               //+3 = 7
+                        SelectiveAcknowledgementPermitted,                            //+2 = 9
+                        SelectiveAcknowledgement((3, 4), [Some((5, 6)), None, None]), // + 18 = 27
+                        Timestamp(5, 6),                                              // + 10 = 37
+                        Noop,
+                        Noop,
+                        Noop,
+                        Noop // + 4
+                    ])
+                );
+                //test with all fields filled of the selective ack
+                assert_eq!(
+                    Err(TcpOptionWriteError::NotEnoughSpace(41)),
+                    header.set_options(&[
+                        Noop,                                                                         // 1
+                        SelectiveAcknowledgement((3, 4), [Some((5, 6)), Some((5, 6)), Some((5, 6))]), // + 34 = 35
+                        MaximumSegmentSize(1), // + 4 = 39
+                        Noop,
+                        Noop // + 2 = 41
+                    ])
+                );
+
+                //test with all fields filled of the selective ack
+                assert_eq!(
+                    Err(TcpOptionWriteError::NotEnoughSpace(41)),
+                    header.set_options(&[
+                        Noop,                                                 // 1
+                        SelectiveAcknowledgement((3, 4), [None, None, None]), // + 10 = 11
+                        Timestamp(1, 2),                                      // + 10 = 21
+                        Timestamp(1, 2),                                      // + 10 = 31
+                        MaximumSegmentSize(1),                                // + 4 = 35
+                        Noop,
+                        Noop,
+                        Noop,
+                        Noop,
+                        Noop,
+                        Noop // + 6 = 41
+                    ])
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn set_options_raw(header in tcp_any()) {
+            let base: TcpHeader = Default::default();
+
+            let dummy = [
+                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+                25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
+            ];
+
+            //ok size -> expect output based on options size
+            for i in 0..40 {
+                let mut header = header.clone();
+                //set the options
+                header.set_options_raw(&dummy[..i]).unwrap();
+
+                //determine the expected options length
+                let mut options_length = i / 4;
+                if i % 4 != 0 {
+                    options_length += 1;
+                }
+                options_length = options_length * 4;
+
+                //expecetd data
+                let mut expected_options = [0; 40];
+                expected_options[..i].copy_from_slice(&dummy[..i]);
+
+                assert_eq!(options_length, header.options.len());
+                assert_eq!(
+                    (options_length / 4) as u8 + TcpHeader::MIN_DATA_OFFSET,
+                    header.data_offset()
+                );
+                assert_eq!(&expected_options[..options_length], header.options.as_slice());
+            }
+
+            //too big -> expect error
+            let mut header = base.clone();
+            use crate::TcpOptionWriteError::*;
+            assert_eq!(
+                Err(NotEnoughSpace(dummy.len())),
+                header.set_options_raw(&dummy[..])
+            );
+        }
+    }
+
+    #[test]
+    fn options_iterator() {
+        let options = [
+            TcpOptionElement::Timestamp(0x00102030, 0x01112131), //10
+            TcpOptionElement::SelectiveAcknowledgement(
+                (0x02122232, 0x03132333),
+                [None, None, None],
+            ), //20
+            TcpOptionElement::Timestamp(0x04142434, 0x05152535), //30
+            TcpOptionElement::Timestamp(0x06162636, 0x07172737), //40
+        ];
+
+        let base: TcpHeader = {
+            let mut base: TcpHeader = Default::default();
+            base.set_options(&options[..]).unwrap();
+            base
+        };
+
+        assert_eq!(
+            &options[..],
+            &base
+                .options_iterator()
+                .map(|x| x.unwrap())
+                .collect::<Vec<TcpOptionElement>>()[..]
+        );
+    }
+
+    proptest! {
+        #[test]
+        #[allow(deprecated)]
+        fn read_from_slice(header in tcp_any()) {
+            // ok case
+            {
+                let bytes = {
+                    let mut bytes = header.to_bytes();
+                    bytes.try_extend_from_slice(
+                        &([0u8;TcpHeader::MAX_LEN])[..bytes.remaining_capacity()]
+                    ).unwrap();
+                    bytes
+                };
+
+                let (actual_header, actual_rest) = TcpHeader::read_from_slice(&bytes[..]).unwrap();
+                assert_eq!(actual_header, header);
+                assert_eq!(actual_rest, &bytes[header.header_len() as usize..]);
+            }
+
+            // data offset error
+            for data_offset in 0..TcpHeader::MIN_DATA_OFFSET {
+                let bytes = {
+                    let mut bytes = header.to_bytes();
+                    bytes[12] = (bytes[12] & 0xf) | ((data_offset << 4) & 0xf0);
+                    bytes
+                };
+                assert_eq!(
+                    TcpHeader::read_from_slice(&bytes[..]),
+                    Err(Content(DataOffsetTooSmall{ data_offset }))
+                );
+            }
+
+            // length error
+            {
+                let bytes = header.to_bytes();
+                for len in 0..(header.header_len() as usize) {
+                    assert_eq!(
+                        TcpHeader::read_from_slice(&bytes[..len])
+                            .unwrap_err(),
+                        Len(err::LenError {
+                            required_len: if len < TcpHeader::MIN_LEN {
+                                TcpHeader::MIN_LEN
+                            } else {
+                                header.header_len() as usize
+                            },
+                            len: len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::TcpHeader,
+                            layer_start_offset: 0,
+                        })
+                    );
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(header in tcp_any()) {
+            // ok case
+            {
+                let bytes = {
+                    let mut bytes = header.to_bytes();
+                    bytes.try_extend_from_slice(
+                        &([0u8;TcpHeader::MAX_LEN])[..bytes.remaining_capacity()]
+                    ).unwrap();
+                    bytes
+                };
+
+                let (actual_header, actual_rest) = TcpHeader::from_slice(&bytes[..]).unwrap();
+                assert_eq!(actual_header, header);
+                assert_eq!(actual_rest, &bytes[header.header_len() as usize..]);
+            }
+
+            // data offset error
+            for data_offset in 0..TcpHeader::MIN_DATA_OFFSET {
+                let bytes = {
+                    let mut bytes = header.to_bytes();
+                    bytes[12] = (bytes[12] & 0b1111) | ((data_offset << 4) & 0b1111_0000);
+                    bytes
+                };
+                assert_eq!(
+                    TcpHeader::from_slice(&bytes[..]),
+                    Err(Content(DataOffsetTooSmall{ data_offset }))
+                );
+            }
+
+            // length error
+            {
+                let bytes = header.to_bytes();
+                for len in 0..(header.header_len() as usize) {
+                    assert_eq!(
+                        TcpHeader::from_slice(&bytes[..len])
+                            .unwrap_err(),
+                        Len(err::LenError {
+                            required_len: if len < TcpHeader::MIN_LEN {
+                                TcpHeader::MIN_LEN
+                            } else {
+                                header.header_len() as usize
+                            },
+                            len: len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::TcpHeader,
+                            layer_start_offset: 0,
+                        })
+                    );
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read(header in tcp_any()) {
+            // ok case
+            {
+                let bytes = header.to_bytes();
+                let mut cursor = Cursor::new(&bytes[..]);
+                let actual = TcpHeader::read(&mut cursor).unwrap();
+                assert_eq!(header.header_len() as u64, cursor.position());
+                assert_eq!(header, actual);
+            }
+
+            // data offset error
+            for data_offset in 0..TcpHeader::MIN_DATA_OFFSET {
+                let bytes = {
+                    let mut bytes = header.to_bytes();
+                    bytes[12] = (bytes[12] & 0xf) | ((data_offset << 4) & 0xf0);
+                    bytes
+                };
+                assert_eq!(
+                    TcpHeader::read(&mut Cursor::new(&bytes[..]))
+                        .unwrap_err()
+                        .content_error()
+                        .unwrap(),
+                    DataOffsetTooSmall{ data_offset }
+                );
+            }
+
+            // length error
+            {
+                let bytes = header.to_bytes();
+                for len in 0..(header.header_len() as usize) {
+                    let mut cursor = Cursor::new(&bytes[..len]);
+                    let err = TcpHeader::read(&mut cursor).unwrap_err();
+                    assert!(err.io_error().is_some());
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write(header in tcp_any()) {
+            // ok
+            {
+                let mut bytes = [0u8;TcpHeader::MAX_LEN];
+                let len = {
+                    let mut cursor = Cursor::new(&mut bytes[..]);
+                    header.write(&mut cursor).unwrap();
+
+                    cursor.position() as usize
+                };
+                assert_eq!(header.header_len() as usize, len);
+                assert_eq!(
+                    header,
+                    TcpHeader::from_slice(&bytes[..len]).unwrap().0
+                );
+            }
+            // length error
+            for len in 0..header.header_len() {
+                let mut bytes = [0u8;TcpHeader::MAX_LEN];
+                let mut cursor = Cursor::new(&mut bytes[..len as usize]);
+                let result = header.write(&mut cursor);
+                assert!(result.is_err());
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_bytes(header in tcp_any()) {
+            let bytes = header.to_bytes();
+            let actual = TcpHeader::from_slice(&bytes).unwrap().0;
+            assert_eq!(actual, header);
+        }
+    }
+
+    #[test]
+    fn calc_checksum_ipv4() {
+        use crate::TcpOptionElement::*;
+
+        // checksum == 0xf (no carries) (aka sum == 0xffff)
+        {
+            let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8];
+            //write the tcp header
+            let tcp = TcpHeader::new(0, 0, 40905, 0);
+            let ip_header = Ipv4Header::new(
+                //payload length
+                tcp.header_len_u16() + (tcp_payload.len() as u16),
+                //time to live
+                0,
+                ip_number::TCP,
+                //source ip address
+                [0; 4],
+                //destination ip address
+                [0; 4],
+            )
+            .unwrap();
+            assert_eq!(Ok(0x0), tcp.calc_checksum_ipv4(&ip_header, &tcp_payload));
+            assert_eq!(
+                Ok(0x0),
+                tcp.calc_checksum_ipv4_raw(ip_header.source, ip_header.destination, &tcp_payload)
+            );
+        }
+
+        //a header with options
+        {
+            let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8];
+
+            let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653);
+            tcp.urgent_pointer = 0xE26E;
+            tcp.ns = true;
+            tcp.fin = true;
+            tcp.syn = true;
+            tcp.rst = true;
+            tcp.psh = true;
+            tcp.ack = true;
+            tcp.ece = true;
+            tcp.urg = true;
+            tcp.cwr = true;
+
+            tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)])
+                .unwrap();
+
+            let ip_header = Ipv4Header::new(
+                //payload length
+                tcp.header_len_u16() + (tcp_payload.len() as u16),
+                //time to live
+                20,
+                //contained protocol is udp
+                ip_number::TCP,
+                //source ip address
+                [192, 168, 1, 42],
+                //destination ip address
+                [192, 168, 1, 1],
+            )
+            .unwrap();
+
+            //check checksum
+            assert_eq!(Ok(0xdeeb), tcp.calc_checksum_ipv4(&ip_header, &tcp_payload));
+        }
+
+        //a header with an uneven number of options
+        {
+            let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8, 9];
+
+            let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653);
+            tcp.urgent_pointer = 0xE26E;
+            tcp.ns = true;
+            tcp.fin = true;
+            tcp.syn = true;
+            tcp.rst = true;
+            tcp.psh = true;
+            tcp.ack = true;
+            tcp.ece = true;
+            tcp.urg = true;
+            tcp.cwr = true;
+
+            tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)])
+                .unwrap();
+
+            let ip_header = Ipv4Header::new(
+                //payload length
+                tcp.header_len_u16() + (tcp_payload.len() as u16),
+                //time to live
+                20,
+                //contained protocol is udp
+                ip_number::TCP,
+                //source ip address
+                [192, 168, 1, 42],
+                //destination ip address
+                [192, 168, 1, 1],
+            )
+            .unwrap();
+
+            //check checksum
+            assert_eq!(Ok(0xd5ea), tcp.calc_checksum_ipv4(&ip_header, &tcp_payload));
+        }
+
+        // value error
+        {
+            // write the udp header
+            let tcp: TcpHeader = Default::default();
+            let len = (core::u16::MAX - tcp.header_len_u16()) as usize + 1;
+            let mut tcp_payload = Vec::with_capacity(len);
+            tcp_payload.resize(len, 0);
+            let ip_header = Ipv4Header::new(0, 0, ip_number::TCP, [0; 4], [0; 4]).unwrap();
+            assert_eq!(
+                Err(ValueTooBigError {
+                    actual: len,
+                    max_allowed: usize::from(core::u16::MAX) - usize::from(tcp.header_len()),
+                    value_type: ValueType::TcpPayloadLengthIpv4,
+                }),
+                tcp.calc_checksum_ipv4(&ip_header, &tcp_payload)
+            );
+        }
+    }
+
+    #[test]
+    fn calc_checksum_ipv4_raw() {
+        // checksum == 0xf (no carries) (aka sum == 0xffff)
+        {
+            let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8];
+            //write the tcp header
+            let tcp = TcpHeader::new(0, 0, 40905, 0);
+            assert_eq!(
+                Ok(0x0),
+                tcp.calc_checksum_ipv4_raw([0; 4], [0; 4], &tcp_payload)
+            );
+        }
+
+        // a header with options
+        {
+            let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8];
+
+            let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653);
+            tcp.urgent_pointer = 0xE26E;
+            tcp.ns = true;
+            tcp.fin = true;
+            tcp.syn = true;
+            tcp.rst = true;
+            tcp.psh = true;
+            tcp.ack = true;
+            tcp.ece = true;
+            tcp.urg = true;
+            tcp.cwr = true;
+
+            tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)])
+                .unwrap();
+
+            // check checksum
+            assert_eq!(
+                Ok(0xdeeb),
+                tcp.calc_checksum_ipv4_raw([192, 168, 1, 42], [192, 168, 1, 1], &tcp_payload)
+            );
+        }
+
+        // a header with an uneven number of options
+        {
+            let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8, 9];
+
+            let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653);
+            tcp.urgent_pointer = 0xE26E;
+            tcp.ns = true;
+            tcp.fin = true;
+            tcp.syn = true;
+            tcp.rst = true;
+            tcp.psh = true;
+            tcp.ack = true;
+            tcp.ece = true;
+            tcp.urg = true;
+            tcp.cwr = true;
+
+            tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)])
+                .unwrap();
+
+            // check checksum
+            assert_eq!(
+                Ok(0xd5ea),
+                tcp.calc_checksum_ipv4_raw([192, 168, 1, 42], [192, 168, 1, 1], &tcp_payload)
+            );
+        }
+
+        // value error
+        {
+            // write the udp header
+            let tcp: TcpHeader = Default::default();
+            let len = (core::u16::MAX - tcp.header_len_u16()) as usize + 1;
+            let mut tcp_payload = Vec::with_capacity(len);
+            tcp_payload.resize(len, 0);
+            assert_eq!(
+                Err(ValueTooBigError {
+                    actual: len,
+                    max_allowed: usize::from(core::u16::MAX) - usize::from(tcp.header_len()),
+                    value_type: ValueType::TcpPayloadLengthIpv4,
+                }),
+                tcp.calc_checksum_ipv4_raw([0; 4], [0; 4], &tcp_payload)
+            );
+        }
+    }
+
+    #[test]
+    fn calc_checksum_ipv6() {
+        // ok case
+        {
+            let tcp_payload = [51, 52, 53, 54, 55, 56, 57, 58];
+
+            // setup tcp header
+            let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653);
+            tcp.urgent_pointer = 0xE26E;
+
+            tcp.ns = true;
+            tcp.fin = true;
+            tcp.syn = true;
+            tcp.rst = true;
+            tcp.psh = true;
+            tcp.ack = true;
+            tcp.ece = true;
+            tcp.urg = true;
+            tcp.cwr = true;
+
+            use crate::TcpOptionElement::*;
+            tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)])
+                .unwrap();
+
+            let ip_header = Ipv6Header {
+                traffic_class: 1,
+                flow_label: 0x81806.try_into().unwrap(),
+                payload_length: tcp_payload.len() as u16 + tcp.header_len_u16(),
+                next_header: ip_number::TCP,
+                hop_limit: 40,
+                source: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+                destination: [
+                    21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+                ],
+            };
+            // check checksum
+            assert_eq!(Ok(0x786e), tcp.calc_checksum_ipv6(&ip_header, &tcp_payload));
+        }
+
+        // error
+        #[cfg(target_pointer_width = "64")]
+        {
+            //write the udp header
+            let tcp: TcpHeader = Default::default();
+            let len = (core::u32::MAX - tcp.header_len() as u32) as usize + 1;
+
+            //lets create a slice of that size that points to zero
+            //(as most systems can not allocate blocks of the size of u32::MAX)
+            let tcp_payload = unsafe {
+                //NOTE: The pointer must be initialized with a non null value
+                //      otherwise a key constraint of slices is not fulfilled
+                //      which can lead to crashes in release mode.
+                use core::ptr::NonNull;
+                core::slice::from_raw_parts(NonNull::<u8>::dangling().as_ptr(), len)
+            };
+            let ip_header = Ipv6Header {
+                traffic_class: 1,
+                flow_label: 0x81806.try_into().unwrap(),
+                payload_length: 0, //lets assume jumbograms behavior (set to 0, as bigger then u16)
+                next_header: ip_number::TCP,
+                hop_limit: 40,
+                source: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+                destination: [
+                    21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+                ],
+            };
+
+            assert_eq!(
+                Err(ValueTooBigError {
+                    actual: len,
+                    max_allowed: core::u32::MAX as usize - usize::from(tcp.header_len()),
+                    value_type: ValueType::TcpPayloadLengthIpv6,
+                }),
+                tcp.calc_checksum_ipv6(&ip_header, &tcp_payload)
+            );
+        }
+    }
+
+    #[test]
+    fn calc_checksum_ipv6_raw() {
+        // ok case
+        {
+            let tcp_payload = [51, 52, 53, 54, 55, 56, 57, 58];
+
+            //write the tcp header
+            let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653);
+            tcp.urgent_pointer = 0xE26E;
+
+            tcp.ns = true;
+            tcp.fin = true;
+            tcp.syn = true;
+            tcp.rst = true;
+            tcp.psh = true;
+            tcp.ack = true;
+            tcp.ece = true;
+            tcp.urg = true;
+            tcp.cwr = true;
+
+            use crate::TcpOptionElement::*;
+            tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)])
+                .unwrap();
+
+            // check checksum
+            assert_eq!(
+                Ok(0x786e),
+                tcp.calc_checksum_ipv6_raw(
+                    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+                    [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,],
+                    &tcp_payload
+                )
+            );
+        }
+
+        // error
+        #[cfg(target_pointer_width = "64")]
+        {
+            //write the udp header
+            let tcp: TcpHeader = Default::default();
+            let len = (core::u32::MAX - tcp.header_len() as u32) as usize + 1;
+
+            //lets create a slice of that size that points to zero
+            //(as most systems can not allocate blocks of the size of u32::MAX)
+            let tcp_payload = unsafe {
+                //NOTE: The pointer must be initialized with a non null value
+                //      otherwise a key constraint of slices is not fulfilled
+                //      which can lead to crashes in release mode.
+                use core::ptr::NonNull;
+                core::slice::from_raw_parts(NonNull::<u8>::dangling().as_ptr(), len)
+            };
+
+            assert_eq!(
+                Err(ValueTooBigError {
+                    actual: len,
+                    max_allowed: core::u32::MAX as usize - usize::from(tcp.header_len()),
+                    value_type: ValueType::TcpPayloadLengthIpv6,
+                }),
+                tcp.calc_checksum_ipv6_raw(
+                    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+                    [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,],
+                    &tcp_payload
+                )
+            );
+        }
+    }
+}
diff --git a/src/transport/tcp_header_slice.rs b/src/transport/tcp_header_slice.rs
new file mode 100644
index 0000000..01889d4
--- /dev/null
+++ b/src/transport/tcp_header_slice.rs
@@ -0,0 +1,976 @@
+use crate::{
+    err::{ValueTooBigError, ValueType},
+    *,
+};
+
+/// A slice containing an tcp header of a network package.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TcpHeaderSlice<'a> {
+    pub(crate) slice: &'a [u8],
+}
+
+impl<'a> TcpHeaderSlice<'a> {
+    /// Creates a slice containing an tcp header.
+    pub fn from_slice(slice: &'a [u8]) -> Result<TcpHeaderSlice<'a>, err::tcp::HeaderSliceError> {
+        use err::tcp::{HeaderError::*, HeaderSliceError::*};
+
+        //check length
+        if slice.len() < TcpHeader::MIN_LEN {
+            return Err(Len(err::LenError {
+                required_len: TcpHeader::MIN_LEN,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::TcpHeader,
+                layer_start_offset: 0,
+            }));
+        }
+
+        // SAFETY:
+        // Safe as it is checked at the start of the function that the
+        // length of the slice is at least TcpHeader::MIN_LEN (20).
+        let header_len = unsafe {
+            // The length of the TCP header can be determined via
+            // the data offset field of the TCP header. "data offset"
+            // stores the offset in 4 byte steps from the start of the
+            // header to the payload of the header.
+            //
+            // "data offset" is stored in the upper 4 bits
+            // (aka 0b1111_0000) of byte 12. To get to total length
+            // in bytes of the header data offset has to be multiplied
+            // by 4. So the naive version to get the length of
+            // the header would be:
+            //
+            // ```
+            // let data_offset = (*slice.get_unchecked(12) & 0xf0) >> 4;
+            // let len = data_offset * 4;
+            // ```
+            //
+            // But a multiplication by 4 can be replaced by 2
+            // left shift:
+            //
+            // ```
+            // let data_offset = (*slice.get_unchecked(12) & 0xf0) >> 4;
+            // let len = data_offset << 2;
+            // ```
+            //
+            // And finally the shifts can be combined to one:
+            //
+            // ```
+            // let len = (*slice.get_unchecked(12) & 0xf0) >> 2;
+            // ```
+            usize::from((*slice.get_unchecked(12) & 0xf0) >> 2)
+        };
+
+        if header_len < TcpHeader::MIN_LEN {
+            Err(Content(DataOffsetTooSmall {
+                data_offset: (header_len >> 2) as u8,
+            }))
+        } else if slice.len() < header_len {
+            Err(Len(err::LenError {
+                required_len: header_len,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::TcpHeader,
+                layer_start_offset: 0,
+            }))
+        } else {
+            //done
+            Ok(TcpHeaderSlice::<'a> {
+                // SAFETY:
+                // Safe as there is a check above that the slice length
+                // is at least len.
+                slice: unsafe { core::slice::from_raw_parts(slice.as_ptr(), header_len) },
+            })
+        }
+    }
+
+    /// Returns the slice containing the tcp header
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+
+    /// Read the source port number.
+    #[inline]
+    pub fn source_port(&self) -> u16 {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr()) }
+    }
+
+    /// Read the destination port number.
+    #[inline]
+    pub fn destination_port(&self) -> u16 {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) }
+    }
+
+    /// Read the sequence number of the first data octet in this segment (except when SYN is present).
+    ///
+    /// If SYN is present the sequence number is the initial sequence number (ISN)
+    /// and the first data octet is ISN+1.
+    /// \[copied from RFC 793, page 16\]
+    #[inline]
+    pub fn sequence_number(&self) -> u32 {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { get_unchecked_be_u32(self.slice.as_ptr().add(4)) }
+    }
+
+    /// Reads the acknowledgment number.
+    ///
+    /// If the ACK control bit is set this field contains the value of the
+    /// next sequence number the sender of the segment is expecting to
+    /// receive.
+    ///
+    /// Once a connection is established this is always sent.
+    #[inline]
+    pub fn acknowledgment_number(&self) -> u32 {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { get_unchecked_be_u32(self.slice.as_ptr().add(8)) }
+    }
+
+    /// Read the number of 32 bit words in the TCP Header.
+    ///
+    /// This indicates where the data begins.  The TCP header (even one including options) is an
+    /// integral number of 32 bits long.
+    #[inline]
+    pub fn data_offset(&self) -> u8 {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { (*self.slice.get_unchecked(12) & 0b1111_0000) >> 4 }
+    }
+
+    /// ECN-nonce - concealment protection (experimental: see RFC 3540)
+    #[inline]
+    pub fn ns(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(12) & 0b0000_0001) }
+    }
+
+    /// Read the fin flag (no more data from sender).
+    #[inline]
+    pub fn fin(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0000_0001) }
+    }
+
+    /// Reads the syn flag (synchronize sequence numbers).
+    #[inline]
+    pub fn syn(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0000_0010) }
+    }
+
+    /// Reads the rst flag (reset the connection).
+    #[inline]
+    pub fn rst(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0000_0100) }
+    }
+
+    /// Reads the psh flag (push function).
+    #[inline]
+    pub fn psh(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0000_1000) }
+    }
+
+    /// Reads the ack flag (acknowledgment field significant).
+    #[inline]
+    pub fn ack(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0001_0000) }
+    }
+
+    /// Reads the urg flag (Urgent Pointer field significant).
+    #[inline]
+    pub fn urg(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0010_0000) }
+    }
+
+    /// Read the ECN-Echo flag (RFC 3168).
+    #[inline]
+    pub fn ece(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0100_0000) }
+    }
+
+    /// Reads the cwr flag (Congestion Window Reduced).
+    ///
+    /// This flag is set by the sending host to indicate that it received a TCP
+    /// segment with the ECE flag set and had responded in congestion control
+    /// mechanism (added to header by RFC 3168).
+    #[inline]
+    pub fn cwr(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(13) & 0b1000_0000) }
+    }
+
+    /// The number of data octets beginning with the one indicated in the
+    /// acknowledgment field which the sender of this segment is willing to
+    /// accept.
+    #[inline]
+    pub fn window_size(&self) -> u16 {
+        u16::from_be_bytes(
+            // SAFETY:
+            // Constructor checks that the slice has at least the length
+            // of 20.
+            unsafe { [*self.slice.get_unchecked(14), *self.slice.get_unchecked(15)] },
+        )
+    }
+
+    /// Checksum (16 bit one's complement) of the pseudo ip header, this tcp header and the payload.
+    #[inline]
+    pub fn checksum(&self) -> u16 {
+        u16::from_be_bytes(
+            // SAFETY:
+            // Constructor checks that the slice has at least the length
+            // of 20.
+            unsafe { [*self.slice.get_unchecked(16), *self.slice.get_unchecked(17)] },
+        )
+    }
+
+    /// This field communicates the current value of the urgent pointer as a
+    /// positive offset from the sequence number in this segment.
+    ///
+    /// The urgent pointer points to the sequence number of the octet following
+    /// the urgent data.  This field is only be interpreted in segments with
+    /// the URG control bit set.
+    #[inline]
+    pub fn urgent_pointer(&self) -> u16 {
+        u16::from_be_bytes(
+            // SAFETY:
+            // Constructor checks that the slice has at least the length
+            // of 20.
+            unsafe { [*self.slice.get_unchecked(18), *self.slice.get_unchecked(19)] },
+        )
+    }
+
+    /// Options of the header
+    #[inline]
+    pub fn options(&self) -> &[u8] {
+        &self.slice[TcpHeader::MIN_LEN..self.data_offset() as usize * 4]
+    }
+
+    /// Returns an iterator that allows to iterate through all known TCP header options.
+    #[inline]
+    pub fn options_iterator(&self) -> TcpOptionsIterator {
+        TcpOptionsIterator::from_slice(self.options())
+    }
+
+    /// Decode all the fields and copy the results to a TcpHeader struct
+    pub fn to_header(&self) -> TcpHeader {
+        TcpHeader {
+            source_port: self.source_port(),
+            destination_port: self.destination_port(),
+            sequence_number: self.sequence_number(),
+            acknowledgment_number: self.acknowledgment_number(),
+            ns: self.ns(),
+            fin: self.fin(),
+            syn: self.syn(),
+            rst: self.rst(),
+            psh: self.psh(),
+            ack: self.ack(),
+            ece: self.ece(),
+            urg: self.urg(),
+            cwr: self.cwr(),
+            window_size: self.window_size(),
+            checksum: self.checksum(),
+            urgent_pointer: self.urgent_pointer(),
+            options: {
+                let options_slice = self.options();
+                let mut options = TcpOptions {
+                    len: options_slice.len() as u8,
+                    buf: [0; 40],
+                };
+                options.buf[..options_slice.len()].clone_from_slice(options_slice);
+                options
+            },
+        }
+    }
+
+    /// Calculates the TCP header checksum based on a ipv4 header and returns the result. This does NOT set the checksum.
+    pub fn calc_checksum_ipv4(
+        &self,
+        ip_header: &Ipv4HeaderSlice,
+        payload: &[u8],
+    ) -> Result<u16, ValueTooBigError<usize>> {
+        self.calc_checksum_ipv4_raw(ip_header.source(), ip_header.destination(), payload)
+    }
+
+    /// Calculates the checksum for the current header in ipv4 mode and returns the result. This does NOT set the checksum.
+    pub fn calc_checksum_ipv4_raw(
+        &self,
+        source_ip: [u8; 4],
+        destination_ip: [u8; 4],
+        payload: &[u8],
+    ) -> Result<u16, ValueTooBigError<usize>> {
+        // check that the total length fits into the field
+        let header_len = self.slice.len() as u16;
+        let max_payload = usize::from(u16::MAX) - usize::from(header_len);
+        if max_payload < payload.len() {
+            return Err(ValueTooBigError {
+                actual: payload.len(),
+                max_allowed: max_payload,
+                value_type: ValueType::TcpPayloadLengthIpv4,
+            });
+        }
+
+        // calculate the checksum
+        let tcp_len = header_len + (payload.len() as u16);
+        Ok(self.calc_checksum_post_ip(
+            checksum::Sum16BitWords::new()
+                .add_4bytes(source_ip)
+                .add_4bytes(destination_ip)
+                .add_2bytes([0, ip_number::TCP.0])
+                .add_2bytes((tcp_len).to_be_bytes()),
+            payload,
+        ))
+    }
+
+    /// Calculates the TCP header checksum based on a ipv6 header and returns the result. This does NOT set the checksum..
+    pub fn calc_checksum_ipv6(
+        &self,
+        ip_header: &Ipv6HeaderSlice,
+        payload: &[u8],
+    ) -> Result<u16, ValueTooBigError<usize>> {
+        self.calc_checksum_ipv6_raw(ip_header.source(), ip_header.destination(), payload)
+    }
+
+    /// Calculates the checksum for the current header in ipv6 mode and returns the result. This does NOT set the checksum.
+    pub fn calc_checksum_ipv6_raw(
+        &self,
+        source: [u8; 16],
+        destination: [u8; 16],
+        payload: &[u8],
+    ) -> Result<u16, ValueTooBigError<usize>> {
+        // check that the total length fits into the field
+        let header_len = self.slice.len() as u32;
+        let max_payload = (u32::MAX as usize) - (header_len as usize);
+        if max_payload < payload.len() {
+            return Err(ValueTooBigError {
+                actual: payload.len(),
+                max_allowed: max_payload,
+                value_type: ValueType::TcpPayloadLengthIpv6,
+            });
+        }
+
+        // calculate the checksum
+        let tcp_len = header_len + (payload.len() as u32);
+        Ok(self.calc_checksum_post_ip(
+            checksum::Sum16BitWords::new()
+                .add_16bytes(source)
+                .add_16bytes(destination)
+                .add_2bytes([0, ip_number::TCP.0])
+                .add_4bytes((tcp_len).to_be_bytes()),
+            payload,
+        ))
+    }
+
+    /// This method takes the sum of the pseudo ip header and calculates the rest of the checksum.
+    fn calc_checksum_post_ip(
+        &self,
+        ip_pseudo_header_sum: checksum::Sum16BitWords,
+        payload: &[u8],
+    ) -> u16 {
+        ip_pseudo_header_sum
+            .add_slice(&self.slice[..16]) //until checksum
+            .add_slice(&self.slice[18..self.slice.len()])
+            .add_slice(payload)
+            .ones_complement()
+            .to_be()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{
+        err::{
+            tcp::{HeaderError::*, HeaderSliceError::*},
+            ValueTooBigError, ValueType,
+        },
+        test_gens::*,
+        TcpOptionElement::*,
+        *,
+    };
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn debug(header in tcp_any()) {
+            let buffer = header.to_bytes();
+            let slice = TcpHeaderSlice::from_slice(&buffer).unwrap();
+            assert_eq!(
+                format!("{:?}", slice),
+                format!("TcpHeaderSlice {{ slice: {:?} }}", slice.slice())
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(header in tcp_any()) {
+            let bytes = header.to_bytes();
+            let slice = TcpHeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(slice.clone(), slice);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(header in tcp_any()) {
+            // ok case
+            {
+                let bytes = {
+                    let mut bytes = header.to_bytes();
+                    bytes.try_extend_from_slice(
+                        &([0u8;TcpHeader::MAX_LEN])[..bytes.remaining_capacity()]
+                    ).unwrap();
+                    bytes
+                };
+
+                let slice = TcpHeaderSlice::from_slice(&bytes[..]).unwrap();
+                assert_eq!(slice.slice(), &bytes[..header.header_len() as usize]);
+                assert_eq!(slice.to_header(), header);
+            }
+
+            // data offset error
+            for data_offset in 0..TcpHeader::MIN_DATA_OFFSET {
+                let bytes = {
+                    let mut bytes = header.to_bytes();
+                    bytes[12] = (bytes[12] & 0xf) | ((data_offset << 4) & 0xf0);
+                    bytes
+                };
+                assert_eq!(
+                    TcpHeaderSlice::from_slice(&bytes[..]),
+                    Err(Content(DataOffsetTooSmall{ data_offset }))
+                );
+            }
+
+            // length error
+            {
+                let bytes = header.to_bytes();
+                for len in 0..(header.header_len() as usize) {
+                    assert_eq!(
+                        TcpHeaderSlice::from_slice(&bytes[..len])
+                            .unwrap_err(),
+                        Len(err::LenError {
+                            required_len: if len < TcpHeader::MIN_LEN {
+                                TcpHeader::MIN_LEN
+                            } else {
+                                header.header_len() as usize
+                            },
+                            len: len,
+                            len_source: LenSource::Slice,
+                            layer: err::Layer::TcpHeader,
+                            layer_start_offset: 0,
+                        })
+                    );
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn getters(header in tcp_any()) {
+            let bytes = header.to_bytes();
+            let slice = TcpHeaderSlice::from_slice(&bytes).unwrap();
+
+            assert_eq!(header.source_port, slice.source_port());
+            assert_eq!(header.destination_port, slice.destination_port());
+            assert_eq!(header.sequence_number, slice.sequence_number());
+            assert_eq!(header.acknowledgment_number, slice.acknowledgment_number());
+            assert_eq!(header.data_offset(), slice.data_offset());
+            assert_eq!(header.ns, slice.ns());
+            assert_eq!(header.fin, slice.fin());
+            assert_eq!(header.syn, slice.syn());
+            assert_eq!(header.rst, slice.rst());
+            assert_eq!(header.psh, slice.psh());
+            assert_eq!(header.ack, slice.ack());
+            assert_eq!(header.urg, slice.urg());
+            assert_eq!(header.ece, slice.ece());
+            assert_eq!(header.cwr, slice.cwr());
+            assert_eq!(header.window_size, slice.window_size());
+            assert_eq!(header.checksum, slice.checksum());
+            assert_eq!(header.urgent_pointer, slice.urgent_pointer());
+            assert_eq!(header.options.as_slice(), slice.options());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn options_iterator(header in tcp_any()) {
+            let bytes = header.to_bytes();
+            let slice = TcpHeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(
+                TcpOptionsIterator::from_slice(header.options.as_slice()),
+                slice.options_iterator()
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_header(header in tcp_any()) {
+            let bytes = header.to_bytes();
+            let slice = TcpHeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(header, slice.to_header());
+        }
+    }
+
+    #[test]
+    fn calc_checksum_ipv4() {
+        // checksum == 0xf (no carries) (aka sum == 0xffff)
+        {
+            let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8];
+
+            // setup headers
+            let tcp = TcpHeader::new(0, 0, 40905, 0);
+            let ip_header = Ipv4Header::new(
+                //payload length
+                tcp.header_len_u16() + (tcp_payload.len() as u16),
+                //time to live
+                0,
+                ip_number::TCP,
+                //source ip address
+                [0; 4],
+                //destination ip address
+                [0; 4],
+            )
+            .unwrap();
+
+            // setup slices
+            let ip_bytes = ip_header.to_bytes();
+            let ip_slice = Ipv4HeaderSlice::from_slice(&ip_bytes).unwrap();
+
+            let tcp_bytes = tcp.to_bytes();
+            let tcp_slice = TcpHeaderSlice::from_slice(&tcp_bytes).unwrap();
+
+            assert_eq!(
+                Ok(0x0),
+                tcp_slice.calc_checksum_ipv4(&ip_slice, &tcp_payload)
+            );
+        }
+
+        //a header with options
+        {
+            let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8];
+
+            let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653);
+            tcp.urgent_pointer = 0xE26E;
+            tcp.ns = true;
+            tcp.fin = true;
+            tcp.syn = true;
+            tcp.rst = true;
+            tcp.psh = true;
+            tcp.ack = true;
+            tcp.ece = true;
+            tcp.urg = true;
+            tcp.cwr = true;
+
+            tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)])
+                .unwrap();
+
+            let ip_header = Ipv4Header::new(
+                //payload length
+                tcp.header_len_u16() + (tcp_payload.len() as u16),
+                //time to live
+                20,
+                ip_number::TCP,
+                //source ip address
+                [192, 168, 1, 42],
+                //destination ip address
+                [192, 168, 1, 1],
+            )
+            .unwrap();
+
+            // setup slices
+            let ip_buffer = ip_header.to_bytes();
+            let ip_slice = Ipv4HeaderSlice::from_slice(&ip_buffer).unwrap();
+
+            let tcp_buffer = tcp.to_bytes();
+            let tcp_slice = TcpHeaderSlice::from_slice(&tcp_buffer).unwrap();
+
+            assert_eq!(
+                Ok(0xdeeb),
+                tcp_slice.calc_checksum_ipv4(&ip_slice, &tcp_payload)
+            );
+        }
+
+        //a header with an uneven number of options
+        {
+            let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8, 9];
+
+            let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653);
+            tcp.urgent_pointer = 0xE26E;
+            tcp.ns = true;
+            tcp.fin = true;
+            tcp.syn = true;
+            tcp.rst = true;
+            tcp.psh = true;
+            tcp.ack = true;
+            tcp.ece = true;
+            tcp.urg = true;
+            tcp.cwr = true;
+
+            tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)])
+                .unwrap();
+
+            let ip_header = Ipv4Header::new(
+                //payload length
+                tcp.header_len_u16() + (tcp_payload.len() as u16),
+                //time to live
+                20,
+                ip_number::TCP,
+                //source ip address
+                [192, 168, 1, 42],
+                //destination ip address
+                [192, 168, 1, 1],
+            )
+            .unwrap();
+
+            // setup slices
+            let ip_buffer = ip_header.to_bytes();
+            let ip_slice = Ipv4HeaderSlice::from_slice(&ip_buffer[..]).unwrap();
+
+            let tcp_buffer = tcp.to_bytes();
+            let tcp_slice = TcpHeaderSlice::from_slice(&tcp_buffer[..]).unwrap();
+
+            assert_eq!(
+                Ok(0xd5ea),
+                tcp_slice.calc_checksum_ipv4(&ip_slice, &tcp_payload)
+            );
+        }
+
+        // value error
+        {
+            // write the tcp header
+            let tcp: TcpHeader = Default::default();
+            let len = (core::u16::MAX - tcp.header_len_u16()) as usize + 1;
+            let mut tcp_payload = Vec::with_capacity(len);
+            tcp_payload.resize(len, 0);
+            let ip_header = Ipv4Header::new(0, 0, ip_number::TCP, [0; 4], [0; 4]).unwrap();
+
+            // setup slices
+            let ip_buffer = ip_header.to_bytes();
+            let ip_slice = Ipv4HeaderSlice::from_slice(&ip_buffer).unwrap();
+
+            let tcp_buffer = tcp.to_bytes();
+            let tcp_slice = TcpHeaderSlice::from_slice(&tcp_buffer).unwrap();
+
+            assert_eq!(
+                tcp_slice.calc_checksum_ipv4(&ip_slice, &tcp_payload),
+                Err(ValueTooBigError {
+                    actual: len,
+                    max_allowed: usize::from(core::u16::MAX - tcp.header_len_u16()),
+                    value_type: ValueType::TcpPayloadLengthIpv4,
+                })
+            );
+        }
+    }
+
+    #[test]
+    fn calc_checksum_ipv4_raw() {
+        // checksum == 0xf (no carries) (aka sum == 0xffff)
+        {
+            let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8];
+
+            // setup headers
+            let tcp = TcpHeader::new(0, 0, 40905, 0);
+
+            // setup slices
+            let tcp_bytes = tcp.to_bytes();
+            let tcp_slice = TcpHeaderSlice::from_slice(&tcp_bytes).unwrap();
+
+            assert_eq!(
+                Ok(0x0),
+                tcp_slice.calc_checksum_ipv4_raw([0; 4], [0; 4], &tcp_payload)
+            );
+        }
+
+        //a header with options
+        {
+            let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8];
+
+            let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653);
+            tcp.urgent_pointer = 0xE26E;
+            tcp.ns = true;
+            tcp.fin = true;
+            tcp.syn = true;
+            tcp.rst = true;
+            tcp.psh = true;
+            tcp.ack = true;
+            tcp.ece = true;
+            tcp.urg = true;
+            tcp.cwr = true;
+
+            tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)])
+                .unwrap();
+
+            // setup slices
+            let tcp_buffer = tcp.to_bytes();
+            let tcp_slice = TcpHeaderSlice::from_slice(&tcp_buffer).unwrap();
+
+            assert_eq!(
+                Ok(0xdeeb),
+                tcp_slice.calc_checksum_ipv4_raw([192, 168, 1, 42], [192, 168, 1, 1], &tcp_payload)
+            );
+        }
+
+        //a header with an uneven number of options
+        {
+            let tcp_payload = [1, 2, 3, 4, 5, 6, 7, 8, 9];
+
+            let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653);
+            tcp.urgent_pointer = 0xE26E;
+            tcp.ns = true;
+            tcp.fin = true;
+            tcp.syn = true;
+            tcp.rst = true;
+            tcp.psh = true;
+            tcp.ack = true;
+            tcp.ece = true;
+            tcp.urg = true;
+            tcp.cwr = true;
+
+            tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)])
+                .unwrap();
+
+            // setup slices
+            let tcp_buffer = tcp.to_bytes();
+            let tcp_slice = TcpHeaderSlice::from_slice(&tcp_buffer[..]).unwrap();
+
+            assert_eq!(
+                Ok(0xd5ea),
+                tcp_slice.calc_checksum_ipv4_raw([192, 168, 1, 42], [192, 168, 1, 1], &tcp_payload)
+            );
+        }
+
+        // value error
+        {
+            // write the tcp header
+            let tcp: TcpHeader = Default::default();
+            let len = (core::u16::MAX - tcp.header_len_u16()) as usize + 1;
+            let mut tcp_payload = Vec::with_capacity(len);
+            tcp_payload.resize(len, 0);
+
+            // setup slices
+            let tcp_buffer = tcp.to_bytes();
+            let tcp_slice = TcpHeaderSlice::from_slice(&tcp_buffer).unwrap();
+
+            assert_eq!(
+                tcp_slice.calc_checksum_ipv4_raw([0; 4], [0; 4], &tcp_payload),
+                Err(ValueTooBigError {
+                    actual: len,
+                    max_allowed: usize::from(core::u16::MAX - tcp.header_len_u16()),
+                    value_type: ValueType::TcpPayloadLengthIpv4,
+                })
+            );
+        }
+    }
+
+    #[test]
+    fn calc_checksum_ipv6() {
+        // ok case
+        {
+            let tcp_payload = [51, 52, 53, 54, 55, 56, 57, 58];
+
+            // setup tcp header
+            let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653);
+            tcp.urgent_pointer = 0xE26E;
+
+            tcp.ns = true;
+            tcp.fin = true;
+            tcp.syn = true;
+            tcp.rst = true;
+            tcp.psh = true;
+            tcp.ack = true;
+            tcp.ece = true;
+            tcp.urg = true;
+            tcp.cwr = true;
+
+            use crate::TcpOptionElement::*;
+            tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)])
+                .unwrap();
+
+            // setup ip header
+            let ip_header = Ipv6Header {
+                traffic_class: 1,
+                flow_label: 0x81806.try_into().unwrap(),
+                payload_length: tcp_payload.len() as u16 + tcp.header_len_u16(),
+                next_header: ip_number::TCP,
+                hop_limit: 40,
+                source: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+                destination: [
+                    21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+                ],
+            };
+
+            // setup slices
+            let ip_buffer = ip_header.to_bytes();
+            let ip_slice = Ipv6HeaderSlice::from_slice(&ip_buffer[..]).unwrap();
+
+            let tcp_bytes = tcp.to_bytes();
+            let tcp_slice = TcpHeaderSlice::from_slice(&tcp_bytes).unwrap();
+
+            // verify checksum
+            assert_eq!(
+                Ok(0x786e),
+                tcp_slice.calc_checksum_ipv6(&ip_slice, &tcp_payload)
+            );
+        }
+
+        // error
+        #[cfg(target_pointer_width = "64")]
+        {
+            //write the udp header
+            let tcp: TcpHeader = Default::default();
+            let len = (core::u32::MAX - tcp.header_len() as u32) as usize + 1;
+
+            //lets create a slice of that size that points to zero
+            //(as most systems can not allocate blocks of the size of u32::MAX)
+            let tcp_payload = unsafe {
+                //NOTE: The pointer must be initialized with a non null value
+                //      otherwise a key constraint of slices is not fulfilled
+                //      which can lead to crashes in release mode.
+                use core::ptr::NonNull;
+                core::slice::from_raw_parts(NonNull::<u8>::dangling().as_ptr(), len)
+            };
+            let ip_header = Ipv6Header {
+                traffic_class: 1,
+                flow_label: 0x81806.try_into().unwrap(),
+                payload_length: 0, //lets assume jumbograms behavior (set to 0, as bigger then u16)
+                next_header: ip_number::TCP,
+                hop_limit: 40,
+                source: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+                destination: [
+                    21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+                ],
+            };
+
+            // setup slices
+            let mut ip_buffer = Vec::new();
+            ip_header.write(&mut ip_buffer).unwrap();
+            let ip_slice = Ipv6HeaderSlice::from_slice(&ip_buffer[..]).unwrap();
+
+            let mut tcp_buffer = Vec::new();
+            tcp.write(&mut tcp_buffer).unwrap();
+            let tcp_slice = TcpHeaderSlice::from_slice(&tcp_buffer[..]).unwrap();
+
+            // check for an error during checksum calc
+            assert_eq!(
+                tcp_slice.calc_checksum_ipv6(&ip_slice, &tcp_payload),
+                Err(ValueTooBigError {
+                    actual: len,
+                    max_allowed: core::u32::MAX as usize - tcp.header_len() as usize,
+                    value_type: ValueType::TcpPayloadLengthIpv6,
+                })
+            );
+        }
+    }
+
+    #[test]
+    fn calc_checksum_ipv6_raw() {
+        // ok case
+        {
+            let tcp_payload = [51, 52, 53, 54, 55, 56, 57, 58];
+
+            //write the tcp header
+            let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653);
+            tcp.urgent_pointer = 0xE26E;
+
+            tcp.ns = true;
+            tcp.fin = true;
+            tcp.syn = true;
+            tcp.rst = true;
+            tcp.psh = true;
+            tcp.ack = true;
+            tcp.ece = true;
+            tcp.urg = true;
+            tcp.cwr = true;
+
+            use crate::TcpOptionElement::*;
+            tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)])
+                .unwrap();
+
+            // setup slice
+            let tcp_buffer = tcp.to_bytes();
+            let tcp_slice = TcpHeaderSlice::from_slice(&tcp_buffer[..]).unwrap();
+
+            // verify checksum
+            assert_eq!(
+                Ok(0x786e),
+                tcp_slice.calc_checksum_ipv6_raw(
+                    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+                    [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,],
+                    &tcp_payload
+                )
+            );
+        }
+
+        // error
+        #[cfg(target_pointer_width = "64")]
+        {
+            //write the udp header
+            let tcp: TcpHeader = Default::default();
+            let len = (core::u32::MAX - tcp.header_len() as u32) as usize + 1;
+
+            //lets create a slice of that size that points to zero
+            //(as most systems can not allocate blocks of the size of u32::MAX)
+            let tcp_payload = unsafe {
+                //NOTE: The pointer must be initialized with a non null value
+                //      otherwise a key constraint of slices is not fulfilled
+                //      which can lead to crashes in release mode.
+                use core::ptr::NonNull;
+                core::slice::from_raw_parts(NonNull::<u8>::dangling().as_ptr(), len)
+            };
+
+            // setup slice
+            let tcp_buffer = tcp.to_bytes();
+            let tcp_slice = TcpHeaderSlice::from_slice(&tcp_buffer).unwrap();
+
+            // expect an length error
+            assert_eq!(
+                tcp_slice.calc_checksum_ipv6_raw(
+                    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+                    [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,],
+                    &tcp_payload
+                ),
+                Err(ValueTooBigError {
+                    actual: len,
+                    max_allowed: core::u32::MAX as usize - tcp.header_len() as usize,
+                    value_type: ValueType::TcpPayloadLengthIpv6,
+                })
+            );
+        }
+    }
+}
diff --git a/src/transport/tcp_option_element.rs b/src/transport/tcp_option_element.rs
new file mode 100644
index 0000000..463a6aa
--- /dev/null
+++ b/src/transport/tcp_option_element.rs
@@ -0,0 +1,74 @@
+/// Different kinds of options that can be present in the options part of a tcp header.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum TcpOptionElement {
+    /// "No-Operation" option.
+    ///
+    /// Description from RFC 793:
+    ///
+    /// This option code may be used between options, for example, to
+    /// align the beginning of a subsequent option on a word boundary.
+    /// There is no guarantee that senders will use this option, so
+    /// receivers must be prepared to process options even if they do
+    /// not begin on a word boundary.
+    Noop,
+    /// "Maximum Segment Size" option.
+    ///
+    /// Description from RFC 793:
+    ///
+    /// If this option is present, then it communicates the maximum
+    /// receive segment size at the TCP which sends this segment.
+    /// This field must only be sent in the initial connection request
+    /// (i.e., in segments with the SYN control bit set).  If this
+    //// option is not used, any segment size is allowed.
+    MaximumSegmentSize(u16),
+    WindowScale(u8),
+    SelectiveAcknowledgementPermitted,
+    SelectiveAcknowledgement((u32, u32), [Option<(u32, u32)>; 3]),
+    ///Timestamp & echo (first number is the sender timestamp, the second the echo timestamp)
+    Timestamp(u32, u32),
+}
+
+#[cfg(test)]
+mod test {
+    use crate::*;
+    use alloc::format;
+
+    #[test]
+    fn clone_eq() {
+        use TcpOptionElement::*;
+        let values = [
+            Noop,
+            MaximumSegmentSize(123),
+            WindowScale(123),
+            SelectiveAcknowledgementPermitted,
+            SelectiveAcknowledgement((1, 2), [Some((3, 4)), Some((5, 6)), None]),
+            Timestamp(123, 456),
+        ];
+        for value in values {
+            assert_eq!(value.clone(), value);
+        }
+    }
+
+    #[test]
+    fn debug() {
+        use TcpOptionElement::*;
+        assert_eq!("Noop", format!("{:?}", Noop));
+        assert_eq!(
+            "MaximumSegmentSize(123)",
+            format!("{:?}", MaximumSegmentSize(123))
+        );
+        assert_eq!("WindowScale(123)", format!("{:?}", WindowScale(123)));
+        assert_eq!(
+            "SelectiveAcknowledgementPermitted",
+            format!("{:?}", SelectiveAcknowledgementPermitted)
+        );
+        assert_eq!(
+            "SelectiveAcknowledgement((1, 2), [Some((3, 4)), Some((5, 6)), None])",
+            format!(
+                "{:?}",
+                SelectiveAcknowledgement((1, 2), [Some((3, 4)), Some((5, 6)), None])
+            )
+        );
+        assert_eq!("Timestamp(123, 456)", format!("{:?}", Timestamp(123, 456)));
+    }
+}
diff --git a/src/transport/tcp_option_impl.rs b/src/transport/tcp_option_impl.rs
new file mode 100644
index 0000000..ba22541
--- /dev/null
+++ b/src/transport/tcp_option_impl.rs
@@ -0,0 +1,72 @@
+#[deprecated(since = "0.10.1", note = "Please use tcp_option::KIND_END instead")]
+/// Deprecated please use [tcp_option::KIND_END] instead.
+pub const TCP_OPTION_ID_END: u8 = 0;
+
+#[deprecated(since = "0.10.1", note = "Please use tcp_option::KIND_NOOP instead")]
+/// Deprecated please use [tcp_option::KIND_NOOP] instead.
+pub const TCP_OPTION_ID_NOP: u8 = 1;
+
+#[deprecated(
+    since = "0.10.1",
+    note = "Please use tcp_option::KIND_MAXIMUM_SEGMENT_SIZE instead"
+)]
+/// Deprecated please use [tcp_option::KIND_MAXIMUM_SEGMENT_SIZE] instead.
+pub const TCP_OPTION_ID_MAXIMUM_SEGMENT_SIZE: u8 = 2;
+
+#[deprecated(
+    since = "0.10.1",
+    note = "Please use tcp_option::KIND_WINDOW_SCALE instead"
+)]
+/// Deprecated please use [tcp_option::KIND_WINDOW_SCALE] instead.
+pub const TCP_OPTION_ID_WINDOW_SCALE: u8 = 3;
+
+#[deprecated(
+    since = "0.10.1",
+    note = "Please use tcp_option::KIND_SELECTIVE_ACK_PERMITTED instead"
+)]
+/// Deprecated please use [tcp_option::KIND_SELECTIVE_ACK_PERMITTED] instead.
+pub const TCP_OPTION_ID_SELECTIVE_ACK_PERMITTED: u8 = 4;
+
+#[deprecated(
+    since = "0.10.1",
+    note = "Please use tcp_option::KIND_SELECTIVE_ACK instead"
+)]
+/// Deprecated please use [tcp_option::KIND_SELECTIVE_ACK] instead.
+pub const TCP_OPTION_ID_SELECTIVE_ACK: u8 = 5;
+
+#[deprecated(
+    since = "0.10.1",
+    note = "Please use tcp_option::KIND_TIMESTAMP instead"
+)]
+/// Deprecated please use [tcp_option::KIND_TIMESTAMP] instead.
+pub const TCP_OPTION_ID_TIMESTAMP: u8 = 8;
+
+/// Module containing the constants for tcp options (id number & sizes).
+pub mod tcp_option {
+    /// `u8` identifying the "end of options list" in the tcp option.
+    pub const KIND_END: u8 = 0;
+    /// `u8` identifying a "no operation" tcp option.
+    pub const KIND_NOOP: u8 = 1;
+    /// `u8` identifying a "maximum segment size" tcp option.
+    pub const KIND_MAXIMUM_SEGMENT_SIZE: u8 = 2;
+    /// `u8` identifying a "window scaling" tcp option.
+    pub const KIND_WINDOW_SCALE: u8 = 3;
+    /// `u8` identifying a "selective acknowledgement permitted" tcp option.
+    pub const KIND_SELECTIVE_ACK_PERMITTED: u8 = 4;
+    /// `u8` identifying a "selective acknowledgement" tcp option.
+    pub const KIND_SELECTIVE_ACK: u8 = 5;
+    /// `u8` identifying a "timestamp and echo of previous timestamp" tcp option.
+    pub const KIND_TIMESTAMP: u8 = 8;
+    /// Length in octets/bytes of the "end" tcp option (includes kind value).
+    pub const LEN_END: u8 = 1;
+    /// Length in octets/bytes of the "no operation" tcp option (includes kind value).
+    pub const LEN_NOOP: u8 = 1;
+    /// Length in octets/bytes of the "maximum segment size" tcp option (includes kind value).
+    pub const LEN_MAXIMUM_SEGMENT_SIZE: u8 = 4;
+    /// Length in octets/bytes of the "window scaling" tcp option (includes kind value).
+    pub const LEN_WINDOW_SCALE: u8 = 3;
+    /// Length in octets/bytes of the "selective acknowledgement permitted" tcp option (includes kind value).
+    pub const LEN_SELECTIVE_ACK_PERMITTED: u8 = 2;
+    /// Length in octets/bytes of the "timestamp and echo of previous timestamp" tcp option (includes kind value).
+    pub const LEN_TIMESTAMP: u8 = 10;
+}
diff --git a/src/transport/tcp_option_read_error.rs b/src/transport/tcp_option_read_error.rs
new file mode 100644
index 0000000..71f98f2
--- /dev/null
+++ b/src/transport/tcp_option_read_error.rs
@@ -0,0 +1,126 @@
+/// Errors that can occour while reading the options of a TCP header.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum TcpOptionReadError {
+    /// Returned if an option id was read, but there was not enough memory in the options left to completely read it.
+    UnexpectedEndOfSlice {
+        option_id: u8,
+        expected_len: u8,
+        actual_len: usize,
+    },
+
+    /// Returned if the option as an unexpected size argument (e.g. != 4 for maximum segment size).
+    UnexpectedSize { option_id: u8, size: u8 },
+
+    /// Returned if an unknown tcp header option is encountered.
+    ///
+    /// The first element is the identifier and the slice contains the rest of data left in the options.
+    UnknownId(u8),
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for TcpOptionReadError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        None
+    }
+}
+
+impl core::fmt::Display for TcpOptionReadError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use TcpOptionReadError::*;
+        match self {
+            UnexpectedEndOfSlice {
+                option_id,
+                expected_len,
+                actual_len,
+            } => {
+                write!(f, "TcpOptionReadError: Not enough memory left in slice to read option of kind {} (expected at least {} bytes, only {} bytes available).", option_id, expected_len, actual_len)
+            }
+            UnexpectedSize { option_id, size } => {
+                write!(f, "TcpOptionReadError: Length value of the option of kind {} had unexpected value {}.", option_id, size)
+            }
+            UnknownId(id) => {
+                write!(
+                    f,
+                    "TcpOptionReadError: Unknown tcp option kind value {}.",
+                    id
+                )
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::*;
+    use alloc::format;
+    use proptest::prelude::*;
+
+    #[test]
+    fn debug() {
+        use TcpOptionReadError::*;
+        assert_eq!(
+            "UnexpectedEndOfSlice { option_id: 1, expected_len: 2, actual_len: 3 }",
+            format!(
+                "{:?}",
+                UnexpectedEndOfSlice {
+                    option_id: 1,
+                    expected_len: 2,
+                    actual_len: 3
+                }
+            )
+        );
+    }
+
+    #[test]
+    fn clone_eq() {
+        use TcpOptionReadError::*;
+        let value = UnexpectedEndOfSlice {
+            option_id: 123,
+            expected_len: 5,
+            actual_len: 4,
+        };
+        assert_eq!(value, value.clone());
+    }
+
+    #[cfg(feature = "std")]
+    proptest! {
+        #[test]
+        fn source(
+            arg_u8_0 in any::<u8>(),
+            arg_u8_1 in any::<u8>(),
+            arg_usize in any::<usize>()
+        ) {
+            use std::error::Error;
+            use crate::TcpOptionReadError::*;
+
+            assert!(UnexpectedEndOfSlice{ option_id: arg_u8_0, expected_len: arg_u8_1, actual_len: arg_usize}.source().is_none());
+            assert!(UnexpectedSize{ option_id: arg_u8_0, size: arg_u8_1 }.source().is_none());
+            assert!(UnknownId(arg_u8_0).source().is_none());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn fmt(
+            arg_u8_0 in any::<u8>(),
+            arg_u8_1 in any::<u8>(),
+            arg_usize in any::<usize>()
+        ) {
+            use crate::TcpOptionReadError::*;
+
+            assert_eq!(
+                &format!("TcpOptionReadError: Not enough memory left in slice to read option of kind {} (expected at least {} bytes, only {} bytes available).", arg_u8_0, arg_u8_1, arg_usize),
+                &format!("{}", UnexpectedEndOfSlice{ option_id: arg_u8_0, expected_len: arg_u8_1, actual_len: arg_usize})
+            );
+            assert_eq!(
+                &format!("TcpOptionReadError: Length value of the option of kind {} had unexpected value {}.", arg_u8_0, arg_u8_1),
+                &format!("{}", UnexpectedSize{ option_id: arg_u8_0, size: arg_u8_1 })
+            );
+            assert_eq!(
+                &format!("TcpOptionReadError: Unknown tcp option kind value {}.", arg_u8_0),
+                &format!("{}", UnknownId(arg_u8_0))
+            );
+        }
+    }
+}
diff --git a/src/transport/tcp_option_write_error.rs b/src/transport/tcp_option_write_error.rs
new file mode 100644
index 0000000..8900ddd
--- /dev/null
+++ b/src/transport/tcp_option_write_error.rs
@@ -0,0 +1,72 @@
+/// Errors that can occour when setting the options of a tcp header.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum TcpOptionWriteError {
+    /// There is not enough memory to store all options in the options section of the header (maximum 40 bytes).
+    ///
+    /// The options size is limited by the 4 bit data_offset field in the header which describes
+    /// the total tcp header size in multiple of 4 bytes. This leads to a maximum size for the options
+    /// part of the header of 4*(15 - 5) (minus 5 for the size of the tcp header itself).
+    NotEnoughSpace(usize),
+}
+
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for TcpOptionWriteError {
+    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+        None
+    }
+}
+
+impl core::fmt::Display for TcpOptionWriteError {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        use TcpOptionWriteError::*;
+        match self {
+            NotEnoughSpace(size) => {
+                write!(f, "TcpOptionWriteError: Not enough memory to store all options in the options section of a tcp header (maximum 40 bytes can be stored, the options would have needed {} bytes).", size)
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::*;
+    use alloc::format;
+    use proptest::prelude::*;
+
+    #[test]
+    fn debug() {
+        use TcpOptionWriteError::*;
+        assert_eq!("NotEnoughSpace(0)", format!("{:?}", NotEnoughSpace(0)));
+    }
+
+    #[test]
+    fn clone_eq() {
+        use TcpOptionWriteError::*;
+        let value = NotEnoughSpace(123);
+        assert_eq!(value, value.clone());
+    }
+
+    #[cfg(feature = "std")]
+    proptest! {
+        #[test]
+        fn source(arg_usize in any::<usize>()) {
+            use std::error::Error;
+            use crate::TcpOptionWriteError::*;
+
+            assert!(NotEnoughSpace(arg_usize).source().is_none());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn fmt(arg_usize in any::<usize>()) {
+            use crate::TcpOptionWriteError::*;
+
+            assert_eq!(
+                &format!("TcpOptionWriteError: Not enough memory to store all options in the options section of a tcp header (maximum 40 bytes can be stored, the options would have needed {} bytes).", arg_usize),
+                &format!("{}", NotEnoughSpace(arg_usize))
+            );
+        }
+    }
+}
diff --git a/src/transport/tcp_options.rs b/src/transport/tcp_options.rs
new file mode 100644
index 0000000..8c2720b
--- /dev/null
+++ b/src/transport/tcp_options.rs
@@ -0,0 +1,892 @@
+use crate::{tcp_option, TcpHeader, TcpOptionElement, TcpOptionWriteError, TcpOptionsIterator};
+
+/// Options present in a TCP header.
+///
+/// # Examples (reading)
+///
+/// The underlying bytes can be accessed via the [`TcpOptions::as_slice`] method:
+///
+/// ```
+/// use etherparse::{
+///     TcpOptions,
+///     tcp_option::{KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, KIND_END}
+/// };
+///
+/// let tcp_options = TcpOptions::from([
+///     KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, 2, KIND_END
+/// ]);
+///
+/// // `as_slice` allows access to the raw encoded data
+/// let slice = tcp_options.as_slice();
+///
+/// assert_eq!(
+///     slice,
+///     [KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, 2, KIND_END]
+/// );
+/// ```
+///
+/// It also possible to iterate over the decoded [`TcpOptionElement`]s
+/// by calling [`TcpOptions::elements_iter`]:
+///
+/// ```
+/// use etherparse::{
+///     TcpOptions,
+///     TcpOptionElement::WindowScale,
+///     tcp_option::{KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, KIND_END}
+/// };
+///
+/// let tcp_options = TcpOptions::from([
+///     KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, 2, KIND_END
+/// ]);
+///
+/// // `elements_iter` allows iteration over the decoded elements
+/// // and decoding errors
+/// let mut iter = tcp_options.elements_iter();
+///
+/// assert_eq!(
+///     iter.collect::<Vec<_>>(),
+///     vec![Ok(WindowScale(2))]
+/// );
+/// ```
+///
+/// # Examples (constructing)
+///
+/// Arrays of type `[u8;4]`, `[u8;8]`, `[u8;12]`, `[u8;16]`, `[u8;20]`,
+/// `[u8;24]`, `[u8;28]`, `[u8;32]`, `[u8;36]`, `[u8;40]` can directly be
+/// converted with the `from` or `into` methods to [`TcpOptions`]:
+///
+/// ```
+/// use etherparse::TcpOptions;
+///
+/// // static sized arrays of size 4,8,... 40 can directly be converted
+/// // via `from` or `into`
+/// let options: TcpOptions = [1,2,3,4].into();
+///
+/// assert_eq!(&options[..], &[1,2,3,4]);
+/// ```
+///
+/// Slices can be converted with `try_from` or `try_into` into [`TcpOptions`].
+/// If the len of 40 bytes is exceeded an error is returned and if the
+/// len is not a multiple of 4 the len is automatically increased to the next
+/// multiple of 4 value and the data filled up with zeroes (equivalent to the
+/// TCP END option):
+///
+/// ```
+/// use etherparse::TcpOptions;
+/// {
+///     let data = [1u8,2,3,4,5,6,7,8];
+///
+///     // slices can be converted into TcpOptions via `try_from` or `try_into`
+///     let options: TcpOptions = (&data[..]).try_into().unwrap();
+///
+///     assert_eq!(options.as_slice(), &data);
+/// }
+/// {
+///     let data = [1u8];
+///
+///     // len is automatically increased to a multiple of 4 (filled
+///     // with 0, also known as the END TCP option).
+///     let options = TcpOptions::try_from(&data[..]).unwrap();
+///
+///     assert_eq!(options.as_slice(), &[1, 0, 0, 0]);
+/// }
+/// {
+///     use etherparse::TcpOptionWriteError::NotEnoughSpace;
+///
+///     let data = [0u8;41]; // 41 bytes
+///
+///     // slices with a len bigger then 40 cause an error
+///     let result = TcpOptions::try_from(&data[..]);
+///     assert_eq!(result, Err(NotEnoughSpace(41)));
+/// }
+/// ```
+///
+/// Slices containing [`TcpOptionElement`]s can also be converted via
+/// `try_from` or `try_into` as long as the encoded elements are within
+/// 40 bytes:
+///
+/// ```
+/// use etherparse::{
+///     TcpOptions,
+///     tcp_option::{KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, KIND_NOOP, KIND_END},
+///     TcpOptionElement::{Noop, WindowScale}
+/// };
+///
+/// let elements = [WindowScale(123), Noop, Noop];
+///
+/// // try_from encodes the options into the "on the wire" format
+/// let options = TcpOptions::try_from(&elements[..]).unwrap();
+///
+/// assert_eq!(
+///     options.as_slice(),
+///     &[
+///         KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, 123, KIND_NOOP,
+///         KIND_NOOP, KIND_END, KIND_END, KIND_END
+///     ]
+/// );
+/// ```
+#[derive(Clone)]
+pub struct TcpOptions {
+    /// Number of bytes in the buffer.
+    pub(crate) len: u8,
+
+    /// Buffer containing the options of the header
+    /// (note that the `len` field defines the actual length). Use
+    /// the options() method if you want to get a slice that has
+    /// the actual length of the options.
+    pub(crate) buf: [u8; 40],
+}
+
+impl TcpOptions {
+    /// Maximum number of bytes that can be part of an TCP options.
+    pub const MAX_LEN: usize = 40;
+
+    /// Constructs a new empty TcpOptions.
+    #[inline]
+    pub fn new() -> TcpOptions {
+        TcpOptions {
+            len: 0,
+            buf: [0; 40],
+        }
+    }
+
+    /// Tries to convert an `u8` slice into [`TcpOptions`].
+    ///
+    /// # Examples
+    ///
+    /// Slices with a length that is a multiple of 4 and a length not
+    /// bigger than 40 can be converted one-to-one:
+    ///
+    /// ```
+    /// use etherparse::TcpOptions;
+    ///
+    /// let data = [1u8,2,3,4,5,6,7,8];
+    /// let options = TcpOptions::try_from_slice(&data[..]).unwrap();
+    /// assert_eq!(options.as_slice(), &data);
+    /// ```
+    ///
+    /// If the length is not a multiple of 4 it is automatically filled
+    /// up with `0` (value of TCP option END) to the next multiple of 4:
+    ///
+    /// ```
+    /// use etherparse::TcpOptions;
+    /// {
+    ///     let data = [1u8];
+    ///     let options = TcpOptions::try_from(&data[..]).unwrap();
+    ///     // 3 bytes of zero added so the len is a multiple of 4
+    ///     assert_eq!(options.as_slice(), &[1, 0, 0, 0]);
+    /// }
+    /// ```
+    ///
+    /// In case more than 40 bytes are passed as input an error is returned:
+    ///
+    /// ```
+    /// use etherparse::{
+    ///     TcpOptions,
+    ///     TcpOptionWriteError::NotEnoughSpace
+    /// };
+    ///
+    /// let data = [0u8;41]; // 41 bytes
+    ///
+    /// // slices with a len bigger then 40 cause an error
+    /// let result = TcpOptions::try_from(&data[..]);
+    /// assert_eq!(result, Err(NotEnoughSpace(41)));
+    /// ```
+    pub fn try_from_slice(slice: &[u8]) -> Result<TcpOptions, TcpOptionWriteError> {
+        // check length
+        if Self::MAX_LEN < slice.len() {
+            Err(TcpOptionWriteError::NotEnoughSpace(slice.len()))
+        } else {
+            let len = slice.len() as u8;
+
+            // reset all to zero to ensure padding
+            Ok(TcpOptions {
+                len: ((len >> 2) << 2)
+                    + if 0 != len & 0b11 {
+                        // NOTE: If the slice length is not a multiple of
+                        // 4 the length is automatically increased to be
+                        // a multiple of 4 and the data is filled up with
+                        // zeroes.
+                        4
+                    } else {
+                        0
+                    },
+                buf: {
+                    let mut buf = [0; 40];
+                    buf[..slice.len()].copy_from_slice(slice);
+                    buf
+                },
+            })
+        }
+    }
+
+    /// Tries to convert [`crate::TcpOptionElement`] into serialized
+    /// form as [`TcpOptions`].
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use etherparse::{
+    ///     TcpOptions,
+    ///     tcp_option::{KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, KIND_NOOP, KIND_END},
+    ///     TcpOptionElement::{Noop, WindowScale}
+    /// };
+    ///
+    /// let elements = [WindowScale(123), Noop, Noop];
+    ///
+    /// // try_from encodes the options into the "on the wire" format
+    /// let options = TcpOptions::try_from_elements(&elements[..]).unwrap();
+    ///
+    /// assert_eq!(
+    ///     options.as_slice(),
+    ///     &[
+    ///         KIND_WINDOW_SCALE, LEN_WINDOW_SCALE, 123, KIND_NOOP,
+    ///         // padding in form of "KIND_END" (0) is automatically added
+    ///         // so the resulting options length is a multiple of 4
+    ///         KIND_NOOP, KIND_END, KIND_END, KIND_END
+    ///     ]
+    /// );
+    /// ```
+    pub fn try_from_elements(
+        elements: &[TcpOptionElement],
+    ) -> Result<TcpOptions, TcpOptionWriteError> {
+        // calculate the required size of the options
+        use crate::TcpOptionElement::*;
+        let required_len = elements.iter().fold(0, |acc, ref x| {
+            acc + match x {
+                Noop => 1,
+                MaximumSegmentSize(_) => 4,
+                WindowScale(_) => 3,
+                SelectiveAcknowledgementPermitted => 2,
+                SelectiveAcknowledgement(_, rest) => rest.iter().fold(10, |acc2, ref y| match y {
+                    None => acc2,
+                    Some(_) => acc2 + 8,
+                }),
+                Timestamp(_, _) => 10,
+            }
+        });
+
+        if Self::MAX_LEN < required_len {
+            Err(TcpOptionWriteError::NotEnoughSpace(required_len))
+        } else {
+            // reset the options to null
+            let mut buf = [0u8; TcpOptions::MAX_LEN];
+            let mut len: usize = 0;
+
+            // write the options to the buffer
+            use tcp_option::*;
+            for element in elements {
+                match element {
+                    Noop => {
+                        buf[len] = KIND_NOOP;
+                        len += 1;
+                    }
+                    MaximumSegmentSize(value) => {
+                        // determine insertion area
+                        let t = &mut buf[len..len + 4];
+
+                        // insert data
+                        let value = value.to_be_bytes();
+                        t[0] = KIND_MAXIMUM_SEGMENT_SIZE;
+                        t[1] = 4;
+                        t[2] = value[0];
+                        t[3] = value[1];
+
+                        len += 4;
+                    }
+                    WindowScale(value) => {
+                        // determine insertion area
+                        let t = &mut buf[len..len + 3];
+
+                        // write data
+                        t[0] = KIND_WINDOW_SCALE;
+                        t[1] = 3;
+                        t[2] = *value;
+
+                        len += 3;
+                    }
+                    SelectiveAcknowledgementPermitted => {
+                        // determine insertion area
+                        let insert = &mut buf[len..len + 2];
+
+                        // write data
+                        insert[0] = KIND_SELECTIVE_ACK_PERMITTED;
+                        insert[1] = 2;
+
+                        len += 2;
+                    }
+                    SelectiveAcknowledgement(first, rest) => {
+                        //write guaranteed data
+                        {
+                            let t = &mut buf[len..len + 10];
+                            len += 10;
+
+                            t[0] = KIND_SELECTIVE_ACK;
+                            //write the length
+                            t[1] = rest.iter().fold(10, |acc, ref y| match y {
+                                None => acc,
+                                Some(_) => acc + 8,
+                            });
+                            // write first
+                            t[2..6].copy_from_slice(&first.0.to_be_bytes());
+                            t[6..10].copy_from_slice(&first.1.to_be_bytes());
+                        }
+                        //write the rest
+                        for v in rest {
+                            match v {
+                                None => {}
+                                Some((a, b)) => {
+                                    // determine insertion area
+                                    let t = &mut buf[len..len + 8];
+
+                                    // insert
+                                    t[0..4].copy_from_slice(&a.to_be_bytes());
+                                    t[4..8].copy_from_slice(&b.to_be_bytes());
+
+                                    len += 8;
+                                }
+                            }
+                        }
+                    }
+                    Timestamp(a, b) => {
+                        let t = &mut buf[len..len + 10];
+
+                        t[0] = KIND_TIMESTAMP;
+                        t[1] = 10;
+                        t[2..6].copy_from_slice(&a.to_be_bytes());
+                        t[6..10].copy_from_slice(&b.to_be_bytes());
+
+                        len += 10;
+                    }
+                }
+            }
+            // set the new data offset
+            if (len > 0) && (0 != len & 0b11) {
+                len = (len & (!0b11)) + 4;
+            }
+            // done
+            Ok(TcpOptions {
+                len: len as u8,
+                buf,
+            })
+        }
+    }
+
+    /// The number of 32 bit words in the TCP Header & TCP header options.
+    ///
+    /// This indicates where the data begins relative to the start of an
+    /// TCP header in multiples of 4 bytes. This number is
+    /// present in the `data_offset` field of the header and defines
+    /// the length of the tcp options present.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use etherparse::TcpOptions;
+    ///
+    /// {
+    ///     let options = TcpOptions::try_from_slice(&[]).unwrap();
+    ///     // in case there are no options the minimum size of the tcp
+    ///     // is returned.
+    ///     assert_eq!(5, options.data_offset());
+    /// }
+    /// {
+    ///     let options = TcpOptions::try_from_slice(&[1,2,3,4,5,6,7,8]).unwrap();
+    ///     // otherwise the base TCP header size plus the number of 4 byte
+    ///     // words in the options is returned
+    ///     assert_eq!(5 + 2, options.data_offset());
+    /// }
+    /// ```
+    #[inline]
+    pub fn data_offset(&self) -> u8 {
+        TcpHeader::MIN_DATA_OFFSET + (self.len >> 2)
+    }
+
+    /// Number of bytes in the buffer as an unsigned 8 bit integer.
+    #[inline]
+    pub fn len_u8(&self) -> u8 {
+        self.len
+    }
+
+    /// Number of bytes in the buffer.
+    #[inline]
+    pub fn len(&self) -> usize {
+        self.len as usize
+    }
+
+    /// Returns true if the options contain no elements.
+    #[inline]
+    pub fn is_empty(&self) -> bool {
+        0 == self.len
+    }
+
+    /// Slice containing the options.
+    #[inline]
+    pub fn as_slice(&self) -> &[u8] {
+        debug_assert!(self.len <= 40);
+        // SAFETY: Safe as all constructing methods verify len to be less then 40.
+        unsafe { core::slice::from_raw_parts(self.buf.as_ptr(), self.len()) }
+    }
+
+    /// Mutable slice containing the options.
+    #[inline]
+    pub fn as_mut_slice(&mut self) -> &mut [u8] {
+        debug_assert!(self.len <= 40);
+        // SAFETY: Safe as all constructing methods verify len to be less then 40.
+        unsafe { core::slice::from_raw_parts_mut(self.buf.as_mut_ptr(), self.len()) }
+    }
+
+    /// Returns an iterator that allows to iterate through the
+    /// decoded option elements.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use etherparse::{
+    ///     TcpOptions,
+    ///     TcpOptionElement::{Noop, WindowScale}
+    /// };
+    ///
+    /// let options = TcpOptions::try_from(&[WindowScale(123), Noop, Noop][..]).unwrap();
+    ///
+    /// let mut v = Vec::with_capacity(3);
+    /// for re in options.elements_iter() {
+    ///     v.push(re);
+    /// }
+    /// assert_eq!(v, vec![Ok(WindowScale(123)), Ok(Noop), Ok(Noop)]);
+    /// ```
+    #[inline]
+    pub fn elements_iter(&self) -> TcpOptionsIterator {
+        TcpOptionsIterator {
+            options: self.as_slice(),
+        }
+    }
+}
+
+impl Default for TcpOptions {
+    #[inline]
+    fn default() -> Self {
+        Self {
+            len: 0,
+            buf: [0; 40],
+        }
+    }
+}
+
+impl core::cmp::Eq for TcpOptions {}
+impl PartialEq for TcpOptions {
+    fn eq(&self, other: &Self) -> bool {
+        self.as_slice() == other.as_slice()
+    }
+}
+
+impl<'a> TryFrom<&'a [u8]> for TcpOptions {
+    type Error = TcpOptionWriteError;
+
+    #[inline]
+    fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
+        TcpOptions::try_from_slice(value)
+    }
+}
+
+impl<'a> TryFrom<&'a [TcpOptionElement]> for TcpOptions {
+    type Error = TcpOptionWriteError;
+
+    #[inline]
+    fn try_from(value: &'a [TcpOptionElement]) -> Result<Self, Self::Error> {
+        TcpOptions::try_from_elements(value)
+    }
+}
+
+impl core::fmt::Debug for TcpOptions {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        self.elements_iter().fmt(f)
+    }
+}
+
+impl core::hash::Hash for TcpOptions {
+    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+        self.as_slice().hash(state);
+    }
+}
+
+impl core::cmp::PartialOrd for TcpOptions {
+    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+        Some(self.as_slice().cmp(other.as_slice()))
+    }
+}
+
+impl core::cmp::Ord for TcpOptions {
+    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+        self.as_slice().cmp(other.as_slice())
+    }
+}
+
+impl core::ops::Deref for TcpOptions {
+    type Target = [u8];
+
+    #[inline]
+    fn deref(&self) -> &[u8] {
+        self.as_slice()
+    }
+}
+
+impl AsRef<TcpOptions> for TcpOptions {
+    #[inline]
+    fn as_ref(&self) -> &TcpOptions {
+        self
+    }
+}
+
+impl AsMut<TcpOptions> for TcpOptions {
+    #[inline]
+    fn as_mut(&mut self) -> &mut TcpOptions {
+        self
+    }
+}
+
+impl AsRef<[u8]> for TcpOptions {
+    #[inline]
+    fn as_ref(&self) -> &[u8] {
+        self.as_slice()
+    }
+}
+
+impl AsMut<[u8]> for TcpOptions {
+    #[inline]
+    fn as_mut(&mut self) -> &mut [u8] {
+        self.as_mut_slice()
+    }
+}
+
+macro_rules! from_static_array {
+    ($x:expr) => {
+        impl From<[u8; $x]> for TcpOptions {
+            #[inline]
+            fn from(values: [u8; $x]) -> Self {
+                let mut result = TcpOptions {
+                    len: $x,
+                    buf: [0; 40],
+                };
+                let r = result.buf.as_mut_ptr() as *mut [u8; $x];
+                unsafe {
+                    *r = values;
+                }
+                result
+            }
+        }
+    };
+}
+
+from_static_array!(4);
+from_static_array!(8);
+from_static_array!(12);
+from_static_array!(16);
+from_static_array!(20);
+from_static_array!(24);
+from_static_array!(28);
+from_static_array!(32);
+from_static_array!(36);
+
+impl From<[u8; 40]> for TcpOptions {
+    fn from(values: [u8; 40]) -> Self {
+        TcpOptions {
+            len: 40,
+            buf: values,
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::tcp_options_any;
+    use core::ops::Deref;
+    use proptest::prelude::*;
+    use std::format;
+
+    #[test]
+    fn new() {
+        assert_eq!(
+            TcpOptions::new(),
+            TcpOptions {
+                len: 0,
+                buf: [0; 40]
+            }
+        );
+    }
+
+    #[test]
+    fn try_from_slice() {
+        let actual = TcpOptions::try_from_slice(&[1, 2, 3, 4][..]);
+        assert_eq!(actual, Ok(TcpOptions::from([1, 2, 3, 4])));
+    }
+
+    #[test]
+    fn try_from_elements() {
+        use crate::tcp_option::KIND_NOOP;
+        use crate::TcpOptionElement::Noop;
+        let actual = TcpOptions::try_from_elements(&[Noop, Noop, Noop, Noop][..]);
+        assert_eq!(
+            actual,
+            Ok(TcpOptions::from([
+                KIND_NOOP, KIND_NOOP, KIND_NOOP, KIND_NOOP
+            ]))
+        );
+    }
+
+    proptest! {
+        #[test]
+        fn data_offset(
+            options in tcp_options_any()
+        ) {
+            assert_eq!(
+                (5 + ((options.len as u64) / 4)) as u8,
+                options.data_offset()
+            );
+        }
+    }
+    proptest! {
+        #[test]
+        fn len(
+            options in tcp_options_any()
+        ) {
+            assert_eq!(options.len(), usize::from(options.len));
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn len_u8(
+            options in tcp_options_any()
+        ) {
+            assert_eq!(options.len_u8(), options.len);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn is_empty(
+            options in tcp_options_any()
+        ) {
+            assert_eq!(options.is_empty(), 0 == options.len);
+        }
+    }
+
+    #[test]
+    fn as_slice() {
+        let options = TcpOptions::from([1, 2, 3, 4]);
+        assert_eq!(options.as_slice(), &[1, 2, 3, 4][..]);
+    }
+
+    #[test]
+    fn as_mut_slice() {
+        let mut options = TcpOptions::from([1, 2, 3, 4]);
+        let r = options.as_mut_slice();
+        r[0] = 5;
+        assert_eq!(options.as_slice(), &[5, 2, 3, 4][..]);
+    }
+
+    #[test]
+    fn options_iterator() {
+        let options = TcpOptions::from([1, 2, 3, 4]);
+        assert_eq!(
+            options.elements_iter(),
+            TcpOptionsIterator {
+                options: &[1, 2, 3, 4][..]
+            }
+        );
+    }
+
+    #[test]
+    fn default() {
+        let actual: TcpOptions = Default::default();
+        assert_eq!(0, actual.len);
+        assert_eq!([0u8; 40], actual.buf);
+    }
+
+    #[test]
+    fn try_from() {
+        // from slice
+        {
+            let actual = TcpOptions::try_from(&[1, 2, 3, 4][..]);
+            assert_eq!(actual, Ok(TcpOptions::from([1, 2, 3, 4])));
+        }
+        // from elements
+        {
+            use crate::tcp_option::KIND_NOOP;
+            use crate::TcpOptionElement::Noop;
+            let actual = TcpOptions::try_from(&[Noop, Noop, Noop, Noop][..]);
+            assert_eq!(
+                actual,
+                Ok(TcpOptions::from([
+                    KIND_NOOP, KIND_NOOP, KIND_NOOP, KIND_NOOP
+                ]))
+            );
+        }
+    }
+
+    #[test]
+    fn debug_fmt() {
+        use crate::tcp_option::KIND_NOOP;
+        let data = [KIND_NOOP, KIND_NOOP, KIND_NOOP, KIND_NOOP];
+        let options = TcpOptions::from(data.clone());
+        assert_eq!(
+            format!("{:?}", TcpOptionsIterator { options: &data[..] }),
+            format!("{:?}", options)
+        );
+    }
+
+    #[test]
+    fn clone_eq_hash_ord() {
+        let a = TcpOptions::from([1u8, 2, 3, 4]);
+        assert_eq!(a, a.clone());
+        assert_ne!(a, TcpOptions::from([5u8, 6, 7, 8]));
+        {
+            use core::hash::{Hash, Hasher};
+            use std::collections::hash_map::DefaultHasher;
+            let a_hash = {
+                let mut hasher = DefaultHasher::new();
+                a.hash(&mut hasher);
+                hasher.finish()
+            };
+            let b_hash = {
+                let mut hasher = DefaultHasher::new();
+                a.hash(&mut hasher);
+                hasher.finish()
+            };
+            assert_eq!(a_hash, b_hash);
+        }
+        {
+            use core::cmp::Ordering;
+            assert_eq!(a.cmp(&a), Ordering::Equal);
+        }
+    }
+
+    #[test]
+    pub fn partial_cmp() {
+        use core::cmp::Ordering;
+        let a = TcpOptions::from([1u8, 2, 3, 4]);
+        assert_eq!(a.partial_cmp(&a), Some(Ordering::Equal));
+    }
+
+    #[test]
+    fn deref() {
+        let a = TcpOptions::from([1u8, 2, 3, 4]);
+        assert_eq!(a.deref(), &[1u8, 2, 3, 4][..]);
+    }
+
+    #[test]
+    fn as_ref() {
+        // TcpOptions ref
+        {
+            let a = TcpOptions::from([1u8, 2, 3, 4]);
+            let b: &TcpOptions = a.as_ref();
+            assert_eq!(b, &TcpOptions::from([1u8, 2, 3, 4]));
+        }
+        // slice ref
+        {
+            let a = TcpOptions::from([1u8, 2, 3, 4]);
+            let b: &[u8] = a.as_ref();
+            assert_eq!(b, &[1u8, 2, 3, 4]);
+        }
+    }
+
+    #[test]
+    fn as_mut() {
+        // TcpOptions ref
+        {
+            let mut a = TcpOptions::from([1u8, 2, 3, 4]);
+            let b: &mut TcpOptions = a.as_mut();
+            *b = TcpOptions::from([5u8, 6, 7, 8]);
+            assert_eq!(a, TcpOptions::from([5u8, 6, 7, 8]));
+        }
+        // slice ref
+        {
+            let mut a = TcpOptions::from([1u8, 2, 3, 4]);
+            let b: &mut [u8] = a.as_mut();
+            assert_eq!(b, &[1u8, 2, 3, 4]);
+            b[0] = 5;
+            assert_eq!(a, TcpOptions::from([5u8, 2, 3, 4]));
+        }
+    }
+
+    #[test]
+    fn from() {
+        assert_eq!(TcpOptions::from([1u8, 2, 3, 4]).as_slice(), &[1u8, 2, 3, 4]);
+        assert_eq!(
+            TcpOptions::from([1u8, 2, 3, 4, 5, 6, 7, 8]).as_slice(),
+            &[1u8, 2, 3, 4, 5, 6, 7, 8]
+        );
+        assert_eq!(
+            TcpOptions::from([1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]).as_slice(),
+            &[1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
+        );
+        assert_eq!(
+            TcpOptions::from([1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]).as_slice(),
+            &[1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
+        );
+        assert_eq!(
+            TcpOptions::from([
+                1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
+            ])
+            .as_slice(),
+            &[1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
+        );
+        assert_eq!(
+            TcpOptions::from([
+                1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+                23, 24
+            ])
+            .as_slice(),
+            &[
+                1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+                23, 24
+            ]
+        );
+        assert_eq!(
+            TcpOptions::from([
+                1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+                23, 24, 25, 26, 27, 28
+            ])
+            .as_slice(),
+            &[
+                1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+                23, 24, 25, 26, 27, 28
+            ]
+        );
+        assert_eq!(
+            TcpOptions::from([
+                1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+                23, 24, 25, 26, 27, 28, 29, 30, 31, 32
+            ])
+            .as_slice(),
+            &[
+                1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+                23, 24, 25, 26, 27, 28, 29, 30, 31, 32
+            ]
+        );
+        assert_eq!(
+            TcpOptions::from([
+                1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+                23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36
+            ])
+            .as_slice(),
+            &[
+                1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+                23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36
+            ]
+        );
+        assert_eq!(
+            TcpOptions::from([
+                1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+                23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40
+            ])
+            .as_slice(),
+            &[
+                1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+                23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40
+            ]
+        );
+    }
+}
diff --git a/src/transport/tcp_options_iterator.rs b/src/transport/tcp_options_iterator.rs
new file mode 100644
index 0000000..defc2ff
--- /dev/null
+++ b/src/transport/tcp_options_iterator.rs
@@ -0,0 +1,454 @@
+use crate::*;
+
+/// Allows iterating over the options after a TCP header.
+#[derive(Clone, Eq, PartialEq)]
+pub struct TcpOptionsIterator<'a> {
+    pub(crate) options: &'a [u8],
+}
+
+impl<'a> TcpOptionsIterator<'a> {
+    /// Creates an options iterator from a slice containing encoded tcp options.
+    pub fn from_slice(options: &'a [u8]) -> TcpOptionsIterator<'a> {
+        TcpOptionsIterator { options }
+    }
+
+    /// Returns the non processed part of the options slice.
+    pub fn rest(&self) -> &'a [u8] {
+        self.options
+    }
+}
+
+impl<'a> Iterator for TcpOptionsIterator<'a> {
+    type Item = Result<TcpOptionElement, TcpOptionReadError>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        use crate::TcpOptionElement::*;
+        use crate::TcpOptionReadError::*;
+
+        let expect_specific_size =
+            |expected_size: u8, slice: &[u8]| -> Result<(), TcpOptionReadError> {
+                let id = slice[0];
+                if slice.len() < expected_size as usize {
+                    Err(UnexpectedEndOfSlice {
+                        option_id: id,
+                        expected_len: expected_size,
+                        actual_len: slice.len(),
+                    })
+                } else if slice[1] != expected_size {
+                    Err(UnexpectedSize {
+                        option_id: slice[0],
+                        size: slice[1],
+                    })
+                } else {
+                    Ok(())
+                }
+            };
+
+        if self.options.is_empty() {
+            None
+        } else {
+            //first determine the result
+            use tcp_option::*;
+            let result = match self.options[0] {
+                //end
+                KIND_END => None,
+                KIND_NOOP => {
+                    self.options = &self.options[1..];
+                    Some(Ok(Noop))
+                }
+                KIND_MAXIMUM_SEGMENT_SIZE => {
+                    match expect_specific_size(LEN_MAXIMUM_SEGMENT_SIZE, self.options) {
+                        Err(value) => Some(Err(value)),
+                        _ => {
+                            // SAFETY:
+                            // Safe as the slice size is checked beforehand to be at
+                            // least of size LEN_MAXIMUM_SEGMENT_SIZE (4).
+                            let value =
+                                unsafe { get_unchecked_be_u16(self.options.as_ptr().add(2)) };
+                            self.options = &self.options[4..];
+                            Some(Ok(MaximumSegmentSize(value)))
+                        }
+                    }
+                }
+                KIND_WINDOW_SCALE => match expect_specific_size(LEN_WINDOW_SCALE, self.options) {
+                    Err(value) => Some(Err(value)),
+                    _ => {
+                        let value = self.options[2];
+                        self.options = &self.options[3..];
+                        Some(Ok(WindowScale(value)))
+                    }
+                },
+                KIND_SELECTIVE_ACK_PERMITTED => {
+                    match expect_specific_size(LEN_SELECTIVE_ACK_PERMITTED, self.options) {
+                        Err(value) => Some(Err(value)),
+                        _ => {
+                            self.options = &self.options[2..];
+                            Some(Ok(SelectiveAcknowledgementPermitted))
+                        }
+                    }
+                }
+                KIND_SELECTIVE_ACK => {
+                    //check that the length field can be read
+                    if self.options.len() < 2 {
+                        Some(Err(UnexpectedEndOfSlice {
+                            option_id: self.options[0],
+                            expected_len: 2,
+                            actual_len: self.options.len(),
+                        }))
+                    } else {
+                        //check that the length is an allowed one for this option
+                        let len = self.options[1];
+                        if len != 10 && len != 18 && len != 26 && len != 34 {
+                            Some(Err(UnexpectedSize {
+                                option_id: self.options[0],
+                                size: len,
+                            }))
+                        } else if self.options.len() < (len as usize) {
+                            Some(Err(UnexpectedEndOfSlice {
+                                option_id: self.options[0],
+                                expected_len: len,
+                                actual_len: self.options.len(),
+                            }))
+                        } else {
+                            let mut acks: [Option<(u32, u32)>; 3] = [None; 3];
+                            // SAFETY:
+                            // This is safe as above the len is checked
+                            // to be at least 10 and the slice len is
+                            // checked to be at least len bytes.
+                            let first = unsafe {
+                                (
+                                    get_unchecked_be_u32(self.options.as_ptr().add(2)),
+                                    get_unchecked_be_u32(self.options.as_ptr().add(6)),
+                                )
+                            };
+                            for (i, item) in acks.iter_mut().enumerate().take(3) {
+                                let offset = 2 + 8 + (i * 8);
+                                // SAFETY:
+                                // len can only be 10, 18, 26 or 34
+                                // therefore if the offset is smaller then the
+                                // len, then at least 8 bytes can be read.
+                                unsafe {
+                                    if offset < (len as usize) {
+                                        *item = Some((
+                                            get_unchecked_be_u32(self.options.as_ptr().add(offset)),
+                                            get_unchecked_be_u32(
+                                                self.options.as_ptr().add(offset + 4),
+                                            ),
+                                        ));
+                                    }
+                                }
+                            }
+                            //iterate the options
+                            self.options = &self.options[len as usize..];
+                            Some(Ok(SelectiveAcknowledgement(first, acks)))
+                        }
+                    }
+                }
+                KIND_TIMESTAMP => {
+                    match expect_specific_size(LEN_TIMESTAMP, self.options) {
+                        Err(value) => Some(Err(value)),
+
+                        _ => unsafe {
+                            let t = Timestamp(
+                                // SAFETY:
+                                // Safe as the len first gets checked to be equal
+                                // LEN_TIMESTAMP (10).
+                                get_unchecked_be_u32(self.options.as_ptr().add(2)),
+                                get_unchecked_be_u32(self.options.as_ptr().add(6)),
+                            );
+                            self.options = &self.options[10..];
+                            Some(Ok(t))
+                        },
+                    }
+                }
+
+                //unknown id
+                _ => Some(Err(UnknownId(self.options[0]))),
+            };
+
+            //in case the result was an error or the end move the slice to an end position
+            match result {
+                None | Some(Err(_)) => {
+                    let len = self.options.len();
+                    self.options = &self.options[len..len];
+                }
+                _ => {}
+            }
+
+            //finally return the result
+            result
+        }
+    }
+}
+
+impl<'a> core::fmt::Debug for TcpOptionsIterator<'a> {
+    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
+        let mut list = fmt.debug_list();
+
+        // create a copy and iterate over all elements
+        for it in self.clone() {
+            match it {
+                Ok(e) => {
+                    list.entry(&e);
+                }
+                Err(e) => {
+                    list.entry(&Result::<(), TcpOptionReadError>::Err(e.clone()));
+                }
+            }
+        }
+
+        list.finish()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{tcp_option::*, *};
+    use alloc::format;
+
+    #[test]
+    fn debug() {
+        use tcp_option::*;
+        #[rustfmt::skip]
+        assert_eq!(
+            "[MaximumSegmentSize(0), WindowScale(0)]",
+            format!(
+                "{:?}",
+                TcpOptionsIterator::from_slice(&[
+                    KIND_MAXIMUM_SEGMENT_SIZE, 4, 0, 0,
+                    KIND_WINDOW_SCALE, 3, 0,
+                    KIND_END,
+                ])
+            )
+        );
+        #[rustfmt::skip]
+        assert_eq!(
+            "[MaximumSegmentSize(0), Err(UnexpectedSize { option_id: 3, size: 0 })]",
+            format!(
+                "{:?}",
+                TcpOptionsIterator::from_slice(&[
+                    KIND_MAXIMUM_SEGMENT_SIZE, 4, 0, 0,
+                    KIND_WINDOW_SCALE, 0, 0, 0,
+                ])
+            )
+        );
+    }
+
+    #[test]
+    fn clone_eq() {
+        use tcp_option::*;
+        let it = TcpOptionsIterator::from_slice(&[KIND_END]);
+        assert_eq!(it, it.clone());
+    }
+
+    #[test]
+    fn from_slice_and_rest() {
+        let buffer = [KIND_NOOP, KIND_NOOP, KIND_MAXIMUM_SEGMENT_SIZE, 4];
+        let it = TcpOptionsIterator::from_slice(&buffer);
+        assert_eq!(it.rest(), &buffer[..]);
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn next() {
+        use crate::TcpOptionElement::*;
+
+        // ok test
+        {
+            fn expect_elements(buffer: &[u8], expected: &[TcpOptionElement]) {
+                // options iterator via from_slice()
+                let mut it = TcpOptionsIterator::from_slice(buffer);
+                for element in expected.iter() {
+                    assert_eq!(element, &it.next().unwrap().unwrap());
+                }
+        
+                //expect no more elements
+                assert_eq!(None, it.next());
+                assert_eq!(0, it.rest().len());
+            }
+
+            // nop & max segment size
+            #[rustfmt::skip]
+            expect_elements(&[
+                    KIND_NOOP, 
+                    KIND_NOOP,
+                    KIND_MAXIMUM_SEGMENT_SIZE, 4, 
+                    0, 1,
+                    KIND_WINDOW_SCALE, 3, 2,
+                    KIND_SELECTIVE_ACK_PERMITTED, 2,
+                    KIND_SELECTIVE_ACK, 10,
+                    0, 0, 0, 10,
+                    0, 0, 0, 11,
+                    KIND_SELECTIVE_ACK, 18, 
+                    0, 0, 0, 12,
+                    0, 0, 0, 13,
+                    0, 0, 0, 14,
+                    0, 0, 0, 15,
+                    KIND_SELECTIVE_ACK, 26, 
+                    0, 0, 0, 16,
+                    0, 0, 0, 17,
+                    0, 0, 0, 18,
+                    0, 0, 0, 19,
+                    0, 0, 0, 20,
+                    0, 0, 0, 21,
+                    KIND_SELECTIVE_ACK, 34, 
+                    0, 0, 0, 22,
+                    0, 0, 0, 23,
+                    0, 0, 0, 24,
+                    0, 0, 0, 25,
+                    0, 0, 0, 26,
+                    0, 0, 0, 27,
+                    0, 0, 0, 28,
+                    0, 0, 0, 29,
+                    KIND_TIMESTAMP, 10, 
+                    0, 0, 0, 30, 
+                    0, 0, 0, 31,
+                    KIND_END, 0, 0, 0, 0
+                ],
+                &[
+                    Noop,
+                    Noop,
+                    MaximumSegmentSize(1),
+                    WindowScale(2),
+                    SelectiveAcknowledgementPermitted,
+                    SelectiveAcknowledgement((10,11), [None, None, None]),
+                    SelectiveAcknowledgement((12,13), [Some((14,15)), None, None]),
+                    SelectiveAcknowledgement((16,17), [Some((18,19)), Some((20,21)), None]),
+                    SelectiveAcknowledgement((22,23), [Some((24,25)), Some((26,27)), Some((28,29))]),
+                    Timestamp(30,31)
+                ]
+            );
+        }
+
+        // unknown id
+        {
+            let data = [255, 2, 0, 0, 0,
+                0, 0, 0, 0, 0, //10
+                0, 0, 0, 0, 0,
+                0, 0, 0, 0, 0, //20
+                0, 0, 0, 0, 0,
+                0, 0, 0, 0, 0, //30
+                0, 0, 0, 0];
+            let mut it = TcpOptionsIterator::from_slice(&data);
+            assert_eq!(Some(Err(TcpOptionReadError::UnknownId(255))), it.next());
+            
+            //expect the iterator slice to be moved to the end
+            assert_eq!(0, it.rest().len());
+            assert_eq!(None, it.next());
+            assert_eq!(0, it.rest().len());
+        }
+
+        // unexpected end of slice
+        {
+            fn expect_unexpected_eos(slice: &[u8]) {
+                for i in 1..slice.len()-1 {
+                    let mut it = TcpOptionsIterator::from_slice(&slice[..i]);
+                    assert_eq!(
+                        Some(
+                            Err(
+                                TcpOptionReadError::UnexpectedEndOfSlice{
+                                    option_id: slice[0],
+                                    expected_len: match slice[0] {
+                                        KIND_MAXIMUM_SEGMENT_SIZE => 4,
+                                        KIND_WINDOW_SCALE => 3,
+                                        KIND_SELECTIVE_ACK_PERMITTED => 2,
+                                        KIND_SELECTIVE_ACK => if i < 2 {
+                                            // the inial check only checks if there
+                                            // is enough data to read the length field
+                                            2
+                                        } else {
+                                            slice[1]
+                                        },
+                                        KIND_TIMESTAMP => 10,
+                                        _ => panic!("not part of the tests"),
+                                    },
+                                    actual_len: i
+                                }
+                            )
+                        ),
+                        it.next()
+                    );
+                    //expect the iterator slice to be moved to the end
+                    assert_eq!(0, it.rest().len());
+                    assert_eq!(None, it.next());
+                }
+            }
+
+            expect_unexpected_eos(&[KIND_MAXIMUM_SEGMENT_SIZE, 4, 0, 0]);
+            expect_unexpected_eos(&[KIND_WINDOW_SCALE, 3, 0]);
+            expect_unexpected_eos(&[KIND_MAXIMUM_SEGMENT_SIZE, 4, 0, 0]);
+            expect_unexpected_eos(&[KIND_SELECTIVE_ACK_PERMITTED, 2]);
+            expect_unexpected_eos(&[KIND_SELECTIVE_ACK, 10, 0, 0, 0,
+                                    0, 0, 0, 0, 0]);
+            expect_unexpected_eos(&[KIND_SELECTIVE_ACK, 18, 0, 0, 0,
+                                    0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0,
+                                    0, 0, 0]);
+            expect_unexpected_eos(&[KIND_SELECTIVE_ACK, 26, 0, 0, 0,
+                                    0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0,
+                                    0]);
+            expect_unexpected_eos(&[KIND_SELECTIVE_ACK, 34, 0, 0, 0,
+                                    0, 0, 0, 0, 0, //10
+                                    0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, //20
+                                    0, 0, 0, 0, 0,
+                                    0, 0, 0, 0, 0, //30
+                                    0, 0, 0, 0]);
+            expect_unexpected_eos(&[KIND_TIMESTAMP, 10, 0, 0, 0,
+                                    0, 0, 0, 0, 0]);
+        }
+
+        // unexpected option size error 
+        {
+            fn expect_unexpected_size(id: u8, size: u8) {
+                let data = [
+                    id, size, 0, 0, 0, 0, 0, 0, 0, 0, //10
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //30
+                    0, 0, 0, 0,
+                ];
+                let mut it = TcpOptionsIterator::from_slice(&data);
+                assert_eq!(
+                    Some(Err(TcpOptionReadError::UnexpectedSize {
+                        option_id: data[0],
+                        size: data[1]
+                    })),
+                    it.next()
+                );
+                //expect the iterator slice to be moved to the end
+                assert_eq!(0, it.rest().len());
+                assert_eq!(None, it.next());
+                assert_eq!(0, it.rest().len());
+            }
+            expect_unexpected_size(KIND_MAXIMUM_SEGMENT_SIZE, 3);
+            expect_unexpected_size(KIND_MAXIMUM_SEGMENT_SIZE, 5);
+        
+            expect_unexpected_size(KIND_WINDOW_SCALE, 2);
+            expect_unexpected_size(KIND_WINDOW_SCALE, 4);
+        
+            expect_unexpected_size(KIND_MAXIMUM_SEGMENT_SIZE, 3);
+            expect_unexpected_size(KIND_MAXIMUM_SEGMENT_SIZE, 5);
+        
+            expect_unexpected_size(KIND_SELECTIVE_ACK_PERMITTED, 1);
+            expect_unexpected_size(KIND_SELECTIVE_ACK_PERMITTED, 3);
+        
+            expect_unexpected_size(KIND_SELECTIVE_ACK, 9);
+            expect_unexpected_size(KIND_SELECTIVE_ACK, 11);
+        
+            expect_unexpected_size(KIND_SELECTIVE_ACK, 17);
+            expect_unexpected_size(KIND_SELECTIVE_ACK, 19);
+        
+            expect_unexpected_size(KIND_SELECTIVE_ACK, 25);
+            expect_unexpected_size(KIND_SELECTIVE_ACK, 27);
+        
+            expect_unexpected_size(KIND_SELECTIVE_ACK, 33);
+            expect_unexpected_size(KIND_SELECTIVE_ACK, 35);
+        
+            expect_unexpected_size(KIND_TIMESTAMP, 9);
+            expect_unexpected_size(KIND_TIMESTAMP, 11);
+        }
+    }
+}
diff --git a/src/transport/tcp_slice.rs b/src/transport/tcp_slice.rs
new file mode 100644
index 0000000..c867a72
--- /dev/null
+++ b/src/transport/tcp_slice.rs
@@ -0,0 +1,721 @@
+use crate::{
+    err::{ValueTooBigError, ValueType},
+    *,
+};
+
+/// Slice containing the TCP header & payload.
+#[derive(Clone, Eq, PartialEq)]
+pub struct TcpSlice<'a> {
+    header_len: usize,
+    slice: &'a [u8],
+}
+
+impl<'a> TcpSlice<'a> {
+    /// Try creating a [`TcpSlice`] from a slice containing the
+    /// TCP header and the TCP payload.
+    pub fn from_slice(slice: &'a [u8]) -> Result<TcpSlice<'a>, err::tcp::HeaderSliceError> {
+        use err::tcp::{HeaderError::*, HeaderSliceError::*};
+
+        // check length
+        if slice.len() < TcpHeader::MIN_LEN {
+            return Err(Len(err::LenError {
+                required_len: TcpHeader::MIN_LEN,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::TcpHeader,
+                layer_start_offset: 0,
+            }));
+        }
+
+        // SAFETY:
+        // Safe as it is checked at the start of the function that the
+        // length of the slice is at least TcpHeader::MIN_LEN (20).
+        let header_len = unsafe {
+            // The length of the TCP header can be determined via
+            // the data offset field of the TCP header. "data offset"
+            // stores the offset in 4 byte steps from the start of the
+            // header to the payload of the header.
+            //
+            // "data offset" is stored in the upper 4 bits
+            // (aka 0b1111_0000) of byte 12. To get to total length
+            // in bytes of the header data offset has to be multiplied
+            // by 4. So the naive version to get the length of
+            // the header would be:
+            //
+            // ```
+            // let data_offset = (*slice.get_unchecked(12) & 0xf0) >> 4;
+            // let len = data_offset * 4;
+            // ```
+            //
+            // But a multiplication by 4 can be replaced by 2
+            // left shift:
+            //
+            // ```
+            // let data_offset = (*slice.get_unchecked(12) & 0xf0) >> 4;
+            // let len = data_offset << 2;
+            // ```
+            //
+            // And finally the shifts can be combined to one:
+            //
+            // ```
+            // let len = (*slice.get_unchecked(12) & 0xf0) >> 2;
+            // ```
+            usize::from((*slice.get_unchecked(12) & 0xf0) >> 2)
+        };
+
+        if header_len < TcpHeader::MIN_LEN {
+            Err(Content(DataOffsetTooSmall {
+                data_offset: (header_len >> 2) as u8,
+            }))
+        } else if slice.len() < header_len {
+            Err(Len(err::LenError {
+                required_len: header_len,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::TcpHeader,
+                layer_start_offset: 0,
+            }))
+        } else {
+            //done
+            Ok(TcpSlice::<'a> { header_len, slice })
+        }
+    }
+
+    /// Returns the slice containing the TCP header and payload.
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+
+    /// Returns the slice containing the TCP header
+    /// (including options).
+    #[inline]
+    pub fn header_slice(&self) -> &'a [u8] {
+        unsafe {
+            // SAFETY: Safe as the slice was verified
+            // to be at least header_len long.
+            core::slice::from_raw_parts(self.slice.as_ptr(), self.header_len)
+        }
+    }
+
+    /// Returns the slice containing the TCP payload.
+    #[inline]
+    pub fn payload(&self) -> &'a [u8] {
+        unsafe {
+            // SAFETY: Safe as the slice was verified
+            // to be at least header_len long.
+            core::slice::from_raw_parts(
+                self.slice.as_ptr().add(self.header_len),
+                self.slice.len() - self.header_len,
+            )
+        }
+    }
+
+    /// Length of the TCP header (including TCP options).
+    #[inline]
+    pub const fn header_len(&self) -> usize {
+        self.header_len
+    }
+
+    /// Read the destination port number in the TCP header.
+    #[inline]
+    pub fn source_port(&self) -> u16 {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr()) }
+    }
+
+    /// Read the destination port number in the TCP header.
+    #[inline]
+    pub fn destination_port(&self) -> u16 {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) }
+    }
+
+    /// Read the sequence number in the TCP header.
+    ///
+    /// If SYN is present the sequence number is the initial sequence number (ISN)
+    /// and the first data octet is ISN+1.
+    /// \[copied from RFC 793, page 16\]
+    #[inline]
+    pub fn sequence_number(&self) -> u32 {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { get_unchecked_be_u32(self.slice.as_ptr().add(4)) }
+    }
+
+    /// Reads the acknowledgment number in the TCP header.
+    ///
+    /// If the ACK control bit is set this field contains the value of the
+    /// next sequence number the sender of the segment is expecting to
+    /// receive.
+    ///
+    /// Once a connection is established this is always sent.
+    #[inline]
+    pub fn acknowledgment_number(&self) -> u32 {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { get_unchecked_be_u32(self.slice.as_ptr().add(8)) }
+    }
+
+    /// Read the number of 32 bit words in the TCP Header.
+    ///
+    /// This indicates where the payload begins. The TCP header
+    /// (even one including options) is an integral number of 32
+    /// bits long.
+    #[inline]
+    pub fn data_offset(&self) -> u8 {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { (*self.slice.get_unchecked(12) & 0b1111_0000) >> 4 }
+    }
+
+    /// ECN-nonce - concealment protection (experimental: see RFC 3540)
+    #[inline]
+    pub fn ns(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(12) & 0b0000_0001) }
+    }
+
+    /// Read the fin flag (no more data from sender).
+    #[inline]
+    pub fn fin(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0000_0001) }
+    }
+
+    /// Reads the syn flag (synchronize sequence numbers).
+    #[inline]
+    pub fn syn(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0000_0010) }
+    }
+
+    /// Reads the rst flag (reset the connection).
+    #[inline]
+    pub fn rst(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0000_0100) }
+    }
+
+    /// Reads the psh flag (push function).
+    #[inline]
+    pub fn psh(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0000_1000) }
+    }
+
+    /// Reads the ack flag (acknowledgment field significant).
+    #[inline]
+    pub fn ack(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0001_0000) }
+    }
+
+    /// Reads the urg flag (Urgent Pointer field significant).
+    #[inline]
+    pub fn urg(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0010_0000) }
+    }
+
+    /// Read the ECN-Echo flag (RFC 3168).
+    #[inline]
+    pub fn ece(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0100_0000) }
+    }
+
+    /// Reads the cwr flag (Congestion Window Reduced).
+    ///
+    /// This flag is set by the sending host to indicate that it received a TCP
+    /// segment with the ECE flag set and had responded in congestion control
+    /// mechanism (added to header by RFC 3168).
+    #[inline]
+    pub fn cwr(&self) -> bool {
+        // SAFETY:
+        // Constructor checks that the slice has at least the length
+        // of 20.
+        unsafe { 0 != (*self.slice.get_unchecked(13) & 0b1000_0000) }
+    }
+
+    /// The number of data octets beginning with the one indicated in the
+    /// acknowledgment field which the sender of this segment is willing to
+    /// accept.
+    #[inline]
+    pub fn window_size(&self) -> u16 {
+        u16::from_be_bytes(
+            // SAFETY:
+            // Constructor checks that the slice has at least the length
+            // of 20.
+            unsafe { [*self.slice.get_unchecked(14), *self.slice.get_unchecked(15)] },
+        )
+    }
+
+    /// Checksum (16 bit one's complement) of the pseudo ip header, this tcp header and the payload.
+    #[inline]
+    pub fn checksum(&self) -> u16 {
+        u16::from_be_bytes(
+            // SAFETY:
+            // Constructor checks that the slice has at least the length
+            // of 20.
+            unsafe { [*self.slice.get_unchecked(16), *self.slice.get_unchecked(17)] },
+        )
+    }
+
+    /// This field communicates the current value of the urgent pointer as a
+    /// positive offset from the sequence number in this segment.
+    ///
+    /// The urgent pointer points to the sequence number of the octet following
+    /// the urgent data.  This field is only be interpreted in segments with
+    /// the URG control bit set.
+    #[inline]
+    pub fn urgent_pointer(&self) -> u16 {
+        u16::from_be_bytes(
+            // SAFETY:
+            // Constructor checks that the slice has at least the length
+            // of 20.
+            unsafe { [*self.slice.get_unchecked(18), *self.slice.get_unchecked(19)] },
+        )
+    }
+
+    /// Options of the header
+    #[inline]
+    pub fn options(&self) -> &[u8] {
+        &self.slice[TcpHeader::MIN_LEN..self.header_len]
+    }
+
+    /// Returns an iterator that allows to iterate through all known TCP header options.
+    #[inline]
+    pub fn options_iterator(&self) -> TcpOptionsIterator {
+        TcpOptionsIterator::from_slice(self.options())
+    }
+
+    /// Decode all the fields and copy the results to a
+    /// [`crate::TcpHeader`]` struct.
+    pub fn to_header(&self) -> TcpHeader {
+        TcpHeader {
+            source_port: self.source_port(),
+            destination_port: self.destination_port(),
+            sequence_number: self.sequence_number(),
+            acknowledgment_number: self.acknowledgment_number(),
+            ns: self.ns(),
+            fin: self.fin(),
+            syn: self.syn(),
+            rst: self.rst(),
+            psh: self.psh(),
+            ack: self.ack(),
+            ece: self.ece(),
+            urg: self.urg(),
+            cwr: self.cwr(),
+            window_size: self.window_size(),
+            checksum: self.checksum(),
+            urgent_pointer: self.urgent_pointer(),
+            options: {
+                let options_slice = self.options();
+                let mut options = TcpOptions {
+                    len: options_slice.len() as u8,
+                    buf: [0; 40],
+                };
+                options.buf[..options_slice.len()].clone_from_slice(options_slice);
+                options
+            },
+        }
+    }
+
+    /// Calculates the checksum for the current header in ipv4 mode and
+    /// returns the result. This does NOT set the checksum.
+    pub fn calc_checksum_ipv4(
+        &self,
+        source_ip: [u8; 4],
+        destination_ip: [u8; 4],
+    ) -> Result<u16, ValueTooBigError<usize>> {
+        // check that the total length fits into the field
+        if usize::from(u16::MAX) < self.slice.len() {
+            return Err(ValueTooBigError {
+                actual: self.slice.len(),
+                max_allowed: usize::from(u16::MAX),
+                value_type: ValueType::TcpPayloadLengthIpv4,
+            });
+        }
+
+        // calculate the checksum
+        Ok(self.calc_checksum_post_ip(
+            checksum::Sum16BitWords::new()
+                .add_4bytes(source_ip)
+                .add_4bytes(destination_ip)
+                .add_2bytes([0, ip_number::TCP.0])
+                .add_2bytes((self.slice.len() as u16).to_be_bytes()),
+        ))
+    }
+
+    /// Calculates the checksum for the current header in ipv6 mode and
+    /// returns the result. This does NOT set the checksum.
+    pub fn calc_checksum_ipv6(
+        &self,
+        source: [u8; 16],
+        destination: [u8; 16],
+    ) -> Result<u16, ValueTooBigError<usize>> {
+        // check that the total length fits into the field
+        #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
+        if (u32::MAX as usize) < self.slice.len() {
+            return Err(ValueTooBigError {
+                actual: self.slice.len(),
+                max_allowed: (u32::MAX as usize),
+                value_type: ValueType::TcpPayloadLengthIpv6,
+            });
+        }
+
+        // calculate the checksum
+        Ok(self.calc_checksum_post_ip(
+            checksum::Sum16BitWords::new()
+                .add_16bytes(source)
+                .add_16bytes(destination)
+                .add_2bytes([0, ip_number::TCP.0])
+                .add_4bytes((self.slice.len() as u32).to_be_bytes()),
+        ))
+    }
+
+    /// This method takes the sum of the pseudo ip header and calculates the rest of the checksum.
+    fn calc_checksum_post_ip(&self, ip_pseudo_header_sum: checksum::Sum16BitWords) -> u16 {
+        ip_pseudo_header_sum
+            .add_slice(&self.slice[..16]) //until checksum
+            .add_slice(&self.slice[18..])
+            .ones_complement()
+            .to_be()
+    }
+}
+
+impl<'a> core::fmt::Debug for TcpSlice<'a> {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        f.debug_struct("TcpSlice")
+            .field("header", &self.to_header())
+            .field("payload", &self.payload())
+            .finish()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn debug_clone_eq(
+            tcp in tcp_any()
+        ) {
+            let payload: [u8;4] = [1,2,3,4];
+            let mut data = Vec::with_capacity(
+                tcp.header_len() as usize +
+                payload.len()
+            );
+            data.extend_from_slice(&tcp.to_bytes());
+            data.extend_from_slice(&payload);
+
+            // decode packet
+            let slice = TcpSlice::from_slice(&data).unwrap();
+
+            // check debug output
+            prop_assert_eq!(
+                format!("{:?}", slice),
+                format!(
+                    "TcpSlice {{ header: {:?}, payload: {:?} }}",
+                    &tcp,
+                    &payload[..]
+                )
+            );
+            prop_assert_eq!(slice.clone(), slice);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            tcp in tcp_any()
+        ) {
+            use err::tcp::{HeaderError::*, HeaderSliceError::*};
+
+            let payload: [u8;4] = [1,2,3,4];
+            let data = {
+                let mut data = Vec::with_capacity(
+                    tcp.header_len() as usize +
+                    payload.len()
+                );
+                data.extend_from_slice(&tcp.to_bytes());
+                data.extend_from_slice(&payload);
+                data
+            };
+
+            // normal decode
+            {
+                let slice = TcpSlice::from_slice(&data).unwrap();
+                assert_eq!(&slice.to_header(), &tcp);
+                assert_eq!(slice.payload(), &payload);
+            }
+
+            // too little data to even decode the header
+            for len in 0..(tcp.header_len() as usize) {
+                assert_eq!(
+                    TcpSlice::from_slice(&data[..len]).unwrap_err(),
+                    Len(err::LenError {
+                        required_len: if len < TcpHeader::MIN_LEN {
+                            TcpHeader::MIN_LEN
+                        } else {
+                            tcp.header_len()
+                        },
+                        len,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::TcpHeader,
+                        layer_start_offset: 0,
+                    })
+                );
+            }
+
+            // data offset smaller then minimum header size
+            {
+                let mut broken_data = data.clone();
+                for data_offset in 0..TcpHeader::MIN_DATA_OFFSET {
+                    // inject a bad data offset
+                    broken_data[12] = data_offset << 4 | ( broken_data[12] & 0b0000_1111);
+                    assert_eq!(
+                        TcpSlice::from_slice(&broken_data).unwrap_err(),
+                        Content(DataOffsetTooSmall { data_offset })
+                    );
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn getters(
+            tcp in tcp_any()
+        ) {
+            let payload: [u8;4] = [1,2,3,4];
+            let data = {
+                let mut data = Vec::with_capacity(
+                    tcp.header_len() as usize +
+                    payload.len()
+                );
+                data.extend_from_slice(&tcp.to_bytes());
+                data.extend_from_slice(&payload);
+                data
+            };
+            let slice = TcpSlice::from_slice(&data).unwrap();
+            assert_eq!(slice.slice(), &data);
+            assert_eq!(slice.header_slice(), &data[..tcp.header_len()]);
+            assert_eq!(slice.payload(), &data[tcp.header_len()..]);
+            assert_eq!(slice.header_len(), tcp.header_len());
+            assert_eq!(slice.source_port(), tcp.source_port);
+            assert_eq!(slice.destination_port(), tcp.destination_port);
+            assert_eq!(slice.sequence_number(), tcp.sequence_number);
+            assert_eq!(slice.acknowledgment_number(), tcp.acknowledgment_number);
+            assert_eq!(slice.data_offset(), tcp.data_offset());
+            assert_eq!(slice.ns(), tcp.ns);
+            assert_eq!(slice.fin(), tcp.fin);
+            assert_eq!(slice.syn(), tcp.syn);
+            assert_eq!(slice.rst(), tcp.rst);
+            assert_eq!(slice.psh(), tcp.psh);
+            assert_eq!(slice.ack(), tcp.ack);
+            assert_eq!(slice.urg(), tcp.urg);
+            assert_eq!(slice.ece(), tcp.ece);
+            assert_eq!(slice.cwr(), tcp.cwr);
+            assert_eq!(slice.window_size(), tcp.window_size);
+            assert_eq!(slice.checksum(), tcp.checksum);
+            assert_eq!(slice.urgent_pointer(), tcp.urgent_pointer);
+            assert_eq!(slice.options(), tcp.options.as_slice());
+            assert_eq!(slice.options_iterator(), tcp.options_iterator());
+            assert_eq!(slice.to_header(), tcp);
+        }
+    }
+
+    #[test]
+    fn calc_checksum_ipv4() {
+        use TcpOptionElement::*;
+
+        // checksum == 0xf (no carries) (aka sum == 0xffff)
+        {
+            let payload = [1, 2, 3, 4, 5, 6, 7, 8];
+            let tcp = TcpHeader::new(0, 0, 40905, 0);
+
+            let mut data = Vec::with_capacity(tcp.header_len() + payload.len());
+            data.extend_from_slice(&tcp.to_bytes());
+            data.extend_from_slice(&payload);
+
+            let tcp_slice = TcpSlice::from_slice(&data).unwrap();
+            assert_eq!(Ok(0x0), tcp_slice.calc_checksum_ipv4([0; 4], [0; 4]));
+        }
+
+        // a header with options
+        {
+            let payload = [1, 2, 3, 4, 5, 6, 7, 8];
+            let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653);
+            tcp.urgent_pointer = 0xE26E;
+            tcp.ns = true;
+            tcp.fin = true;
+            tcp.syn = true;
+            tcp.rst = true;
+            tcp.psh = true;
+            tcp.ack = true;
+            tcp.ece = true;
+            tcp.urg = true;
+            tcp.cwr = true;
+            tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)])
+                .unwrap();
+
+            let mut data = Vec::with_capacity(tcp.header_len() + payload.len());
+            data.extend_from_slice(&tcp.to_bytes());
+            data.extend_from_slice(&payload);
+
+            let tcp_slice = TcpSlice::from_slice(&data).unwrap();
+
+            assert_eq!(
+                Ok(0xdeeb),
+                tcp_slice.calc_checksum_ipv4([192, 168, 1, 42], [192, 168, 1, 1])
+            );
+        }
+
+        //a header with an uneven number of options
+        {
+            let payload = [1, 2, 3, 4, 5, 6, 7, 8, 9];
+            let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653);
+            tcp.urgent_pointer = 0xE26E;
+            tcp.ns = true;
+            tcp.fin = true;
+            tcp.syn = true;
+            tcp.rst = true;
+            tcp.psh = true;
+            tcp.ack = true;
+            tcp.ece = true;
+            tcp.urg = true;
+            tcp.cwr = true;
+            tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)])
+                .unwrap();
+
+            let mut data = Vec::with_capacity(tcp.header_len() + payload.len());
+            data.extend_from_slice(&tcp.to_bytes());
+            data.extend_from_slice(&payload);
+
+            let slice = TcpSlice::from_slice(&data[..]).unwrap();
+
+            assert_eq!(
+                Ok(0xd5ea),
+                slice.calc_checksum_ipv4([192, 168, 1, 42], [192, 168, 1, 1])
+            );
+        }
+
+        // value error
+        {
+            // write the tcp header
+            let tcp: TcpHeader = Default::default();
+            let mut data = Vec::with_capacity(usize::from(core::u16::MAX) + 1);
+            data.extend_from_slice(&tcp.to_bytes());
+            data.resize(usize::from(core::u16::MAX) + 1, 0); // payload
+
+            let slice = TcpSlice::from_slice(&data).unwrap();
+
+            assert_eq!(
+                slice.calc_checksum_ipv4([0; 4], [0; 4]),
+                Err(ValueTooBigError {
+                    actual: data.len(),
+                    max_allowed: usize::from(core::u16::MAX),
+                    value_type: ValueType::TcpPayloadLengthIpv4,
+                })
+            );
+        }
+    }
+
+    #[test]
+    fn calc_checksum_ipv6() {
+        use crate::TcpOptionElement::*;
+
+        // ok case
+        {
+            let payload = [51, 52, 53, 54, 55, 56, 57, 58];
+            let mut tcp = TcpHeader::new(69, 42, 0x24900448, 0x3653);
+            tcp.urgent_pointer = 0xE26E;
+            tcp.ns = true;
+            tcp.fin = true;
+            tcp.syn = true;
+            tcp.rst = true;
+            tcp.psh = true;
+            tcp.ack = true;
+            tcp.ece = true;
+            tcp.urg = true;
+            tcp.cwr = true;
+            tcp.set_options(&[Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708)])
+                .unwrap();
+
+            let mut data = Vec::with_capacity(tcp.header_len() + payload.len());
+            data.extend_from_slice(&tcp.to_bytes());
+            data.extend_from_slice(&payload);
+
+            let slice = TcpSlice::from_slice(&data).unwrap();
+            assert_eq!(
+                Ok(0x786e),
+                slice.calc_checksum_ipv6(
+                    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+                    [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,],
+                )
+            );
+        }
+
+        // error
+        #[cfg(target_pointer_width = "64")]
+        {
+            let slice = TcpSlice {
+                header_len: TcpHeader::MIN_LEN,
+                // lets create a slice of that size that points to zero
+                // (as most systems can not allocate blocks of the size of u32::MAX)
+                slice: unsafe {
+                    //NOTE: The pointer must be initialized with a non null value
+                    //      otherwise a key constraint of slices is not fulfilled
+                    //      which can lead to crashes in release mode.
+                    use core::ptr::NonNull;
+                    core::slice::from_raw_parts(
+                        NonNull::<u8>::dangling().as_ptr(),
+                        (core::u32::MAX as usize) + 1,
+                    )
+                },
+            };
+
+            // expect an length error
+            assert_eq!(
+                slice.calc_checksum_ipv6(
+                    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
+                    [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,],
+                ),
+                Err(ValueTooBigError {
+                    actual: (core::u32::MAX as usize) + 1,
+                    max_allowed: core::u32::MAX as usize,
+                    value_type: ValueType::TcpPayloadLengthIpv6,
+                })
+            );
+        }
+    }
+}
diff --git a/src/transport/transport_header.rs b/src/transport/transport_header.rs
new file mode 100644
index 0000000..2fca8c7
--- /dev/null
+++ b/src/transport/transport_header.rs
@@ -0,0 +1,668 @@
+use crate::{
+    err::{packet::TransportChecksumError, ValueTooBigError},
+    *,
+};
+
+/// The possible headers on the transport layer
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum TransportHeader {
+    Udp(UdpHeader),
+    Tcp(TcpHeader),
+    Icmpv4(Icmpv4Header),
+    Icmpv6(Icmpv6Header),
+}
+
+impl TransportHeader {
+    /// Returns Result::Some containing the udp header if self has the value Udp.
+    /// Otherwise None is returned.
+    pub fn udp(self) -> Option<UdpHeader> {
+        use crate::TransportHeader::*;
+        if let Udp(value) = self {
+            Some(value)
+        } else {
+            None
+        }
+    }
+
+    /// Returns Result::Some containing the udp header if self has the value Udp.
+    /// Otherwise None is returned.
+    pub fn mut_udp(&mut self) -> Option<&mut UdpHeader> {
+        use crate::TransportHeader::*;
+        if let Udp(value) = self {
+            Some(value)
+        } else {
+            None
+        }
+    }
+
+    /// Returns Result::Some containing the tcp header if self has the value Tcp.
+    /// Otherwise None is returned.
+    pub fn tcp(self) -> Option<TcpHeader> {
+        use crate::TransportHeader::*;
+        if let Tcp(value) = self {
+            Some(value)
+        } else {
+            None
+        }
+    }
+
+    /// Returns Result::Some containing a mutable reference to the tcp header if self has the value Tcp.
+    /// Otherwise None is returned.
+    pub fn mut_tcp(&mut self) -> Option<&mut TcpHeader> {
+        use crate::TransportHeader::*;
+        if let Tcp(value) = self {
+            Some(value)
+        } else {
+            None
+        }
+    }
+
+    /// Returns Result::Some containing the ICMPv4 header if self has the value Icmpv4.
+    /// Otherwise None is returned.
+    pub fn icmpv4(self) -> Option<Icmpv4Header> {
+        use crate::TransportHeader::*;
+        if let Icmpv4(value) = self {
+            Some(value)
+        } else {
+            None
+        }
+    }
+
+    /// Returns Result::Some containing the ICMPv4 header if self has the value Icmpv4.
+    /// Otherwise None is returned.
+    pub fn mut_icmpv4(&mut self) -> Option<&mut Icmpv4Header> {
+        use crate::TransportHeader::*;
+        if let Icmpv4(value) = self {
+            Some(value)
+        } else {
+            None
+        }
+    }
+
+    /// Returns Result::Some containing the ICMPv6 header if self has the value Icmpv6.
+    /// Otherwise None is returned.
+    pub fn icmpv6(self) -> Option<Icmpv6Header> {
+        use crate::TransportHeader::*;
+        if let Icmpv6(value) = self {
+            Some(value)
+        } else {
+            None
+        }
+    }
+
+    /// Returns Result::Some containing the ICMPv6 header if self has the value Icmpv6.
+    /// Otherwise None is returned.
+    pub fn mut_icmpv6(&mut self) -> Option<&mut Icmpv6Header> {
+        use crate::TransportHeader::*;
+        if let Icmpv6(value) = self {
+            Some(value)
+        } else {
+            None
+        }
+    }
+
+    /// Returns the size of the transport header (in case of UDP fixed,
+    /// in case of TCP cotanining the options).
+    pub fn header_len(&self) -> usize {
+        use crate::TransportHeader::*;
+        match self {
+            Udp(_) => UdpHeader::LEN,
+            Tcp(value) => value.header_len(),
+            Icmpv4(value) => value.header_len(),
+            Icmpv6(value) => value.header_len(),
+        }
+    }
+
+    /// Calculates the checksum for the transport header & sets it in the header for
+    /// an ipv4 header.
+    pub fn update_checksum_ipv4(
+        &mut self,
+        ip_header: &Ipv4Header,
+        payload: &[u8],
+    ) -> Result<(), TransportChecksumError> {
+        use crate::{err::packet::TransportChecksumError::*, TransportHeader::*};
+        match self {
+            Udp(header) => {
+                header.checksum = header
+                    .calc_checksum_ipv4(ip_header, payload)
+                    .map_err(PayloadLen)?;
+            }
+            Tcp(header) => {
+                header.checksum = header
+                    .calc_checksum_ipv4(ip_header, payload)
+                    .map_err(PayloadLen)?;
+            }
+            Icmpv4(header) => {
+                header.update_checksum(payload);
+            }
+            Icmpv6(_) => return Err(Icmpv6InIpv4),
+        }
+        Ok(())
+    }
+
+    /// Calculates the checksum for the transport header & sets it in the header for
+    /// an ipv6 header.
+    pub fn update_checksum_ipv6(
+        &mut self,
+        ip_header: &Ipv6Header,
+        payload: &[u8],
+    ) -> Result<(), ValueTooBigError<usize>> {
+        use crate::TransportHeader::*;
+        match self {
+            Icmpv4(header) => header.update_checksum(payload),
+            Icmpv6(header) => {
+                header.update_checksum(ip_header.source, ip_header.destination, payload)?
+            }
+            Udp(header) => {
+                header.checksum = header.calc_checksum_ipv6(ip_header, payload)?;
+            }
+            Tcp(header) => {
+                header.checksum = header.calc_checksum_ipv6(ip_header, payload)?;
+            }
+        }
+        Ok(())
+    }
+
+    /// Write the transport header to the given writer.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
+        use crate::TransportHeader::*;
+        match self {
+            Icmpv4(value) => value.write(writer),
+            Icmpv6(value) => value.write(writer),
+            Udp(value) => value.write(writer),
+            Tcp(value) => value.write(writer),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{test_gens::*, *};
+    use alloc::{format, vec::Vec};
+    use core::slice;
+    use proptest::prelude::*;
+    use std::io::Cursor;
+
+    proptest! {
+        #[test]
+        fn debug(
+            tcp in tcp_any(),
+            udp in udp_any(),
+            icmpv4 in icmpv4_header_any(),
+            icmpv6 in icmpv6_header_any(),
+        ) {
+            use TransportHeader::*;
+            assert_eq!(
+                format!("Udp({:?})", udp),
+                format!("{:?}", Udp(udp.clone())),
+            );
+            assert_eq!(
+                format!("Tcp({:?})", tcp),
+                format!("{:?}", Tcp(tcp.clone())),
+            );
+            assert_eq!(
+                format!("Icmpv4({:?})", icmpv4),
+                format!("{:?}", Icmpv4(icmpv4.clone())),
+            );
+            assert_eq!(
+                format!("Icmpv6({:?})", icmpv6),
+                format!("{:?}", Icmpv6(icmpv6.clone())),
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(
+            tcp in tcp_any(),
+            udp in udp_any(),
+            icmpv4 in icmpv4_header_any(),
+            icmpv6 in icmpv6_header_any(),
+        ) {
+            use TransportHeader::*;
+            let values = [
+                Udp(udp),
+                Tcp(tcp),
+                Icmpv4(icmpv4),
+                Icmpv6(icmpv6),
+            ];
+            for value in values {
+                assert_eq!(value.clone(), value);
+            }
+        }
+    }
+
+    #[test]
+    fn udp() {
+        let udp: UdpHeader = Default::default();
+        assert_eq!(Some(udp.clone()), TransportHeader::Udp(udp).udp());
+        assert_eq!(None, TransportHeader::Tcp(Default::default()).udp());
+    }
+    #[test]
+    fn mut_udp() {
+        let udp: UdpHeader = Default::default();
+        assert_eq!(Some(&mut udp.clone()), TransportHeader::Udp(udp).mut_udp());
+        assert_eq!(None, TransportHeader::Tcp(Default::default()).mut_udp());
+    }
+    #[test]
+    fn tcp() {
+        let tcp: TcpHeader = Default::default();
+        assert_eq!(Some(tcp.clone()), TransportHeader::Tcp(tcp).tcp());
+        assert_eq!(None, TransportHeader::Udp(Default::default()).tcp());
+    }
+    #[test]
+    fn mut_tcp() {
+        let tcp: TcpHeader = Default::default();
+        assert_eq!(Some(&mut tcp.clone()), TransportHeader::Tcp(tcp).mut_tcp());
+        assert_eq!(None, TransportHeader::Udp(Default::default()).mut_tcp());
+    }
+    proptest! {
+        #[test]
+        fn icmpv4(icmpv4 in icmpv4_header_any()) {
+            assert_eq!(Some(icmpv4.clone()), TransportHeader::Icmpv4(icmpv4).icmpv4());
+            assert_eq!(None, TransportHeader::Udp(Default::default()).icmpv4());
+        }
+    }
+    proptest! {
+        #[test]
+        fn mut_icmpv4(icmpv4 in icmpv4_header_any()) {
+            assert_eq!(Some(&mut icmpv4.clone()), TransportHeader::Icmpv4(icmpv4).mut_icmpv4());
+            assert_eq!(None, TransportHeader::Udp(Default::default()).mut_icmpv4());
+        }
+    }
+    proptest! {
+        #[test]
+        fn icmpv6(icmpv6 in icmpv6_header_any()) {
+            assert_eq!(Some(icmpv6.clone()), TransportHeader::Icmpv6(icmpv6).icmpv6());
+            assert_eq!(None, TransportHeader::Udp(Default::default()).icmpv6());
+        }
+    }
+    proptest! {
+        #[test]
+        fn mut_icmpv6(icmpv6 in icmpv6_header_any()) {
+            assert_eq!(Some(&mut icmpv6.clone()), TransportHeader::Icmpv6(icmpv6).mut_icmpv6());
+            assert_eq!(None, TransportHeader::Udp(Default::default()).mut_icmpv6());
+        }
+    }
+    proptest! {
+        #[test]
+        fn header_size(
+            udp in udp_any(),
+            tcp in tcp_any(),
+            icmpv4 in icmpv4_header_any(),
+            icmpv6 in icmpv6_header_any(),
+        ) {
+            assert_eq!(
+                TransportHeader::Udp(udp).header_len(),
+                UdpHeader::LEN
+            );
+            assert_eq!(
+                TransportHeader::Tcp(tcp.clone()).header_len(),
+                tcp.header_len() as usize
+            );
+            assert_eq!(
+                TransportHeader::Icmpv4(icmpv4.clone()).header_len(),
+                icmpv4.header_len()
+            );
+            assert_eq!(
+                TransportHeader::Icmpv6(icmpv6.clone()).header_len(),
+                icmpv6.header_len()
+            );
+        }
+    }
+    proptest! {
+        #[test]
+        fn update_checksum_ipv4(
+            ipv4 in ipv4_any(),
+            udp in udp_any(),
+            tcp in tcp_any(),
+            icmpv4 in icmpv4_header_any(),
+            icmpv6 in icmpv6_header_any()
+        ) {
+            use TransportHeader::*;
+            use crate::err::{ValueTooBigError, ValueType, packet::TransportChecksumError::*};
+
+            // udp
+            {
+                // ok case
+                {
+                    let mut transport = Udp(udp.clone());
+                    let payload = Vec::new();
+                    transport.update_checksum_ipv4(&ipv4, &payload).unwrap();
+                    assert_eq!(transport.udp().unwrap().checksum,
+                               udp.calc_checksum_ipv4(&ipv4, &payload).unwrap());
+                }
+                // error case
+                {
+                    let mut transport = Udp(udp.clone());
+                    let len = (core::u16::MAX as usize) - UdpHeader::LEN + 1;
+                    let tcp_payload = unsafe {
+                        //NOTE: The pointer must be initialized with a non null value
+                        //      otherwise a key constraint of slices is not fulfilled
+                        //      which can lead to crashes in release mode.
+                        use core::ptr::NonNull;
+                        slice::from_raw_parts(
+                            NonNull::<u8>::dangling().as_ptr(),
+                            len
+                        )
+                    };
+                    assert_eq!(
+                        transport.update_checksum_ipv4(&ipv4, &tcp_payload),
+                        Err(PayloadLen(ValueTooBigError{
+                            actual: len,
+                            max_allowed: (core::u16::MAX as usize) - UdpHeader::LEN,
+                            value_type: ValueType::UdpPayloadLengthIpv4
+                        }))
+                    );
+                }
+            }
+            // tcp
+            {
+                //ok case
+                {
+                    let mut transport = Tcp(tcp.clone());
+                    let payload = Vec::new();
+                    transport.update_checksum_ipv4(&ipv4, &payload).unwrap();
+                    assert_eq!(transport.tcp().unwrap().checksum,
+                               tcp.calc_checksum_ipv4(&ipv4, &payload).unwrap());
+                }
+                //error case
+                {
+                    let mut transport = Tcp(tcp.clone());
+                    let len = (core::u16::MAX - tcp.header_len_u16()) as usize + 1;
+                    let tcp_payload = unsafe {
+                        //NOTE: The pointer must be initialized with a non null value
+                        //      otherwise a key constraint of slices is not fulfilled
+                        //      which can lead to crashes in release mode.
+                        use core::ptr::NonNull;
+                        slice::from_raw_parts(
+                            NonNull::<u8>::dangling().as_ptr(),
+                            len
+                        )
+                    };
+                    assert_eq!(
+                        transport.update_checksum_ipv4(&ipv4, &tcp_payload),
+                        Err(PayloadLen(ValueTooBigError{
+                            actual: len,
+                            max_allowed: (core::u16::MAX as usize) - usize::from(tcp.header_len()),
+                            value_type: ValueType::TcpPayloadLengthIpv4
+                        }))
+                    );
+                }
+            }
+
+            // icmpv4
+            {
+                let mut transport = Icmpv4(icmpv4.clone());
+                let payload = Vec::new();
+                transport.update_checksum_ipv4(&ipv4, &payload).unwrap();
+                assert_eq!(
+                    transport.icmpv4().unwrap().checksum,
+                    icmpv4.icmp_type.calc_checksum(&payload)
+                );
+            }
+
+            // icmpv6 (error)
+            assert_eq!(
+                Icmpv6(icmpv6).update_checksum_ipv4(&ipv4, &[]),
+                Err(Icmpv6InIpv4)
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        #[cfg(target_pointer_width = "64")]
+        fn update_checksum_ipv6(
+            ipv6 in ipv6_any(),
+            udp in udp_any(),
+            tcp in tcp_any(),
+            icmpv4 in icmpv4_header_any(),
+            icmpv6 in icmpv6_header_any(),
+        ) {
+            use TransportHeader::*;
+            use crate::err::{ValueTooBigError, ValueType};
+
+            // udp
+            {
+                //ok case
+                {
+                    let mut transport = Udp(udp.clone());
+                    let payload = Vec::new();
+                    transport.update_checksum_ipv6(&ipv6, &payload).unwrap();
+                    assert_eq!(transport.udp().unwrap().checksum,
+                               udp.calc_checksum_ipv6(&ipv6, &payload).unwrap());
+                }
+                //error case
+                {
+                    let mut transport = Udp(udp.clone());
+                    let len = (core::u32::MAX as usize) - UdpHeader::LEN + 1;
+                    let payload = unsafe {
+                        //NOTE: The pointer must be initialized with a non null value
+                        //      otherwise a key constraint of slices is not fulfilled
+                        //      which can lead to crashes in release mode.
+                        use core::ptr::NonNull;
+                        slice::from_raw_parts(
+                            NonNull::<u8>::dangling().as_ptr(),
+                            len
+                        )
+                    };
+                    assert_eq!(
+                        transport.update_checksum_ipv6(&ipv6, &payload),
+                        Err(ValueTooBigError{
+                            actual: len,
+                            max_allowed: (core::u32::MAX as usize) - UdpHeader::LEN,
+                            value_type: ValueType::UdpPayloadLengthIpv6
+                        })
+                    );
+                }
+            }
+
+            // tcp
+            {
+                //ok case
+                {
+                    let mut transport = Tcp(tcp.clone());
+                    let payload = Vec::new();
+                    transport.update_checksum_ipv6(&ipv6, &payload).unwrap();
+                    assert_eq!(transport.tcp().unwrap().checksum,
+                               tcp.calc_checksum_ipv6(&ipv6, &payload).unwrap());
+                }
+                //error case
+                {
+                    let mut transport = Tcp(tcp.clone());
+                    let len = (core::u32::MAX - tcp.header_len() as u32) as usize + 1;
+                    let tcp_payload = unsafe {
+                        //NOTE: The pointer must be initialized with a non null value
+                        //      otherwise a key constraint of slices is not fulfilled
+                        //      which can lead to crashes in release mode.
+                        use core::ptr::NonNull;
+                        slice::from_raw_parts(
+                            NonNull::<u8>::dangling().as_ptr(),
+                            len
+                        )
+                    };
+                    assert_eq!(
+                        transport.update_checksum_ipv6(&ipv6, &tcp_payload),
+                        Err(ValueTooBigError{
+                            actual: len,
+                            max_allowed: (core::u32::MAX - tcp.header_len() as u32) as usize,
+                            value_type: ValueType::TcpPayloadLengthIpv6
+                        })
+                    );
+                }
+            }
+
+            // icmpv4
+            {
+                let mut transport = Icmpv4(icmpv4.clone());
+                let payload = Vec::new();
+                transport.update_checksum_ipv6(&ipv6, &payload).unwrap();
+                assert_eq!(
+                    transport.icmpv4().unwrap().checksum,
+                    icmpv4.icmp_type.calc_checksum(&payload)
+                );
+            }
+
+            // icmpv6
+            {
+                // normal case
+                {
+                    let mut transport = Icmpv6(icmpv6.clone());
+                    let payload = Vec::new();
+                    transport.update_checksum_ipv6(&ipv6, &payload).unwrap();
+                    assert_eq!(
+                        transport.icmpv6().unwrap().checksum,
+                        icmpv6.icmp_type.calc_checksum(ipv6.source, ipv6.destination, &payload).unwrap()
+                    );
+                }
+
+                // error case
+                {
+                    let mut transport = Icmpv6(icmpv6.clone());
+                    // SAFETY: In case the error is not triggered
+                    //         a segmentation fault will be triggered.
+                    let too_big_slice = unsafe {
+                        //NOTE: The pointer must be initialized with a non null value
+                        //      otherwise a key constraint of slices is not fulfilled
+                        //      which can lead to crashes in release mode.
+                        use core::ptr::NonNull;
+                        core::slice::from_raw_parts(
+                            NonNull::<u8>::dangling().as_ptr(),
+                            (core::u32::MAX - 7) as usize
+                        )
+                    };
+                    assert_eq!(
+                        transport.update_checksum_ipv6(&ipv6, too_big_slice),
+                        Err(ValueTooBigError{
+                            actual: too_big_slice.len(),
+                            max_allowed: (core::u32::MAX - 8) as usize,
+                            value_type: ValueType::Icmpv6PayloadLength,
+                        })
+                    );
+                }
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write(
+            udp in udp_any(),
+            tcp in tcp_any(),
+            icmpv4 in icmpv4_header_any(),
+            icmpv6 in icmpv6_header_any(),
+        ) {
+            // udp
+            {
+                //write
+                {
+                    let result_input = {
+                        let mut buffer = Vec::new();
+                        udp.write(&mut buffer).unwrap();
+                        buffer
+                    };
+                    let result_transport = {
+                        let mut buffer = Vec::new();
+                        TransportHeader::Udp(udp.clone()).write(&mut buffer).unwrap();
+                        buffer
+                    };
+                    assert_eq!(result_input, result_transport);
+                }
+                //trigger an error
+                {
+                    let mut a: [u8;0] = [];
+                    assert!(
+                        TransportHeader::Udp(udp.clone())
+                        .write(&mut Cursor::new(&mut a[..]))
+                        .is_err()
+                    );
+                }
+            }
+            // tcp
+            {
+                //write
+                {
+                    let result_input = {
+                        let mut buffer = Vec::new();
+                        tcp.write(&mut buffer).unwrap();
+                        buffer
+                    };
+                    let result_transport = {
+                        let mut buffer = Vec::new();
+                        TransportHeader::Tcp(tcp.clone()).write(&mut buffer).unwrap();
+                        buffer
+                    };
+                    assert_eq!(result_input, result_transport);
+                }
+                //trigger an error
+                {
+                    let mut a: [u8;0] = [];
+                    assert!(
+                        TransportHeader::Tcp(tcp.clone())
+                        .write(&mut Cursor::new(&mut a[..]))
+                        .is_err()
+                    );
+                }
+            }
+
+            // icmpv4
+            {
+                // normal write
+                {
+                    let result_input = {
+                        let mut buffer = Vec::new();
+                        icmpv4.write(&mut buffer).unwrap();
+                        buffer
+                    };
+                    let result_transport = {
+                        let mut buffer = Vec::new();
+                        TransportHeader::Icmpv4(icmpv4.clone()).write(&mut buffer).unwrap();
+                        buffer
+                    };
+                    assert_eq!(result_input, result_transport);
+                }
+
+                // error during write
+                {
+                    let mut a: [u8;0] = [];
+                    assert!(
+                        TransportHeader::Icmpv4(icmpv4.clone())
+                        .write(&mut Cursor::new(&mut a[..]))
+                        .is_err()
+                    );
+                }
+            }
+
+            // icmpv6
+            {
+                // normal write
+                {
+                    let result_input = {
+                        let mut buffer = Vec::new();
+                        icmpv6.write(&mut buffer).unwrap();
+                        buffer
+                    };
+                    let result_transport = {
+                        let mut buffer = Vec::new();
+                        TransportHeader::Icmpv6(icmpv6.clone()).write(&mut buffer).unwrap();
+                        buffer
+                    };
+                    assert_eq!(result_input, result_transport);
+                }
+
+                // error during write
+                {
+                    let mut a: [u8;0] = [];
+                    assert!(
+                        TransportHeader::Icmpv6(icmpv6.clone())
+                        .write(&mut Cursor::new(&mut a[..]))
+                        .is_err()
+                    );
+                }
+            }
+        }
+    }
+}
diff --git a/src/transport/transport_slice.rs b/src/transport/transport_slice.rs
new file mode 100644
index 0000000..8cfab0b
--- /dev/null
+++ b/src/transport/transport_slice.rs
@@ -0,0 +1,57 @@
+use crate::*;
+
+/// Slice containing UDP, TCP, ICMP or ICMPv4 header & payload.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum TransportSlice<'a> {
+    /// A slice containing an Icmp4 header & payload.
+    Icmpv4(Icmpv4Slice<'a>),
+
+    /// A slice containing an Icmp6 header & payload.
+    Icmpv6(Icmpv6Slice<'a>),
+
+    /// A slice containing an UDP header & payload.
+    Udp(UdpSlice<'a>),
+
+    /// A slice containing a TCP header & payload.
+    Tcp(TcpSlice<'a>),
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use alloc::{format, vec::Vec};
+
+    #[test]
+    fn debug_clone_eq() {
+        // udp
+        {
+            let header: UdpHeader = Default::default();
+            let raw = header.to_bytes();
+            let u = UdpSlice::from_slice(&raw).unwrap();
+            let slice = TransportSlice::Udp(u.clone());
+
+            // clone & eq
+            assert_eq!(slice.clone(), slice);
+
+            // debug
+            assert_eq!(format!("{:?}", slice), format!("Udp({:?})", u));
+        }
+        // tcp
+        {
+            let header: TcpHeader = Default::default();
+            let buffer = {
+                let mut buffer = Vec::with_capacity(header.header_len() as usize);
+                header.write(&mut buffer).unwrap();
+                buffer
+            };
+            let t = TcpSlice::from_slice(&buffer).unwrap();
+            let slice = TransportSlice::Tcp(t.clone());
+
+            // clone & eq
+            assert_eq!(slice.clone(), slice);
+
+            // debug
+            assert_eq!(format!("{:?}", slice), format!("Tcp({:?})", t));
+        }
+    }
+}
diff --git a/src/transport/udp_header.rs b/src/transport/udp_header.rs
new file mode 100644
index 0000000..d4a5279
--- /dev/null
+++ b/src/transport/udp_header.rs
@@ -0,0 +1,1099 @@
+use crate::{err::ValueTooBigError, *};
+
+/// Udp header according to rfc768.
+#[derive(Clone, Debug, Eq, PartialEq, Default)]
+pub struct UdpHeader {
+    /// Source port of the packet (optional).
+    pub source_port: u16,
+    /// Destination port of the packet.
+    pub destination_port: u16,
+    /// Length of the packet (includes the udp header length of 8 bytes).
+    pub length: u16,
+    /// The checksum of the packet. The checksum is calculated from a pseudo header, the udp header and the payload. The pseudo header is composed of source and destination address, protocol number
+    pub checksum: u16,
+}
+
+impl UdpHeader {
+    /// Serialized size of an UDP header in bytes/octets.
+    pub const LEN: usize = 8;
+
+    /// Serialized size of an UDP header in bytes/octets in an [`u16`].
+    pub const LEN_U16: u16 = 8;
+
+    #[deprecated(since = "0.14.0", note = "Use `UdpHeader::LEN` instead")]
+    pub const SERIALIZED_SIZE: usize = UdpHeader::LEN;
+
+    /// Returns an udp header for the given parameters
+    pub fn without_ipv4_checksum(
+        source_port: u16,
+        destination_port: u16,
+        payload_length: usize,
+    ) -> Result<UdpHeader, ValueTooBigError<usize>> {
+        // check that the total length fits into the field
+        const MAX_PAYLOAD_LENGTH: usize = (u16::MAX as usize) - UdpHeader::LEN;
+        if MAX_PAYLOAD_LENGTH < payload_length {
+            return Err(ValueTooBigError {
+                actual: payload_length,
+                max_allowed: MAX_PAYLOAD_LENGTH,
+                value_type: err::ValueType::UdpPayloadLengthIpv4,
+            });
+        }
+
+        Ok(UdpHeader {
+            source_port,
+            destination_port,
+            length: (UdpHeader::LEN + payload_length) as u16, //payload plus udp header
+            checksum: 0,
+        })
+    }
+
+    /// Calculate an udp header given an ipv4 header and the payload
+    pub fn with_ipv4_checksum(
+        source_port: u16,
+        destination_port: u16,
+        ip_header: &Ipv4Header,
+        payload: &[u8],
+    ) -> Result<UdpHeader, ValueTooBigError<usize>> {
+        // check that the total length fits into the field
+        const MAX_PAYLOAD_LENGTH: usize = (u16::MAX as usize) - UdpHeader::LEN;
+        if MAX_PAYLOAD_LENGTH < payload.len() {
+            return Err(ValueTooBigError {
+                actual: payload.len(),
+                max_allowed: MAX_PAYLOAD_LENGTH,
+                value_type: err::ValueType::UdpPayloadLengthIpv4,
+            });
+        }
+
+        let mut result = UdpHeader {
+            source_port,
+            destination_port,
+            length: (UdpHeader::LEN + payload.len()) as u16, //payload plus udp header
+            checksum: 0,
+        };
+        result.checksum =
+            result.calc_checksum_ipv4_internal(ip_header.source, ip_header.destination, payload);
+        Ok(result)
+    }
+
+    /// Calculates the upd header checksum based on a ipv4 header.
+    pub fn calc_checksum_ipv4(
+        &self,
+        ip_header: &Ipv4Header,
+        payload: &[u8],
+    ) -> Result<u16, ValueTooBigError<usize>> {
+        self.calc_checksum_ipv4_raw(ip_header.source, ip_header.destination, payload)
+    }
+
+    /// Calculates the upd header checksum based on a ipv4 header.
+    pub fn calc_checksum_ipv4_raw(
+        &self,
+        source: [u8; 4],
+        destination: [u8; 4],
+        payload: &[u8],
+    ) -> Result<u16, ValueTooBigError<usize>> {
+        // check that the total length fits into the field
+        const MAX_PAYLOAD_LENGTH: usize = (u16::MAX as usize) - UdpHeader::LEN;
+        if MAX_PAYLOAD_LENGTH < payload.len() {
+            return Err(ValueTooBigError {
+                actual: payload.len(),
+                max_allowed: MAX_PAYLOAD_LENGTH,
+                value_type: err::ValueType::UdpPayloadLengthIpv4,
+            });
+        }
+
+        Ok(self.calc_checksum_ipv4_internal(source, destination, payload))
+    }
+
+    /// Calculates the upd header checksum based on a ipv4 header.
+    fn calc_checksum_ipv4_internal(
+        &self,
+        source: [u8; 4],
+        destination: [u8; 4],
+        payload: &[u8],
+    ) -> u16 {
+        self.calc_checksum_post_ip(
+            //pseudo header
+            checksum::Sum16BitWords::new()
+                .add_4bytes(source)
+                .add_4bytes(destination)
+                .add_2bytes([0, ip_number::UDP.0])
+                .add_2bytes(self.length.to_be_bytes()),
+            payload,
+        )
+    }
+
+    /// Calculate an udp header given an ipv6 header and the payload
+    pub fn with_ipv6_checksum(
+        source_port: u16,
+        destination_port: u16,
+        ip_header: &Ipv6Header,
+        payload: &[u8],
+    ) -> Result<UdpHeader, ValueTooBigError<usize>> {
+        // check that the total length fits into the field
+        const MAX_PAYLOAD_LENGTH: usize = (u16::MAX as usize) - UdpHeader::LEN;
+        if MAX_PAYLOAD_LENGTH < payload.len() {
+            return Err(ValueTooBigError {
+                actual: payload.len(),
+                max_allowed: MAX_PAYLOAD_LENGTH,
+                value_type: err::ValueType::UdpPayloadLengthIpv6,
+            });
+        }
+
+        let mut result = UdpHeader {
+            source_port,
+            destination_port,
+            length: (UdpHeader::LEN + payload.len()) as u16, //payload plus udp header
+            checksum: 0,
+        };
+        result.checksum =
+            result.calc_checksum_ipv6_internal(ip_header.source, ip_header.destination, payload);
+        Ok(result)
+    }
+
+    /// Calculates the checksum of the current udp header given an ipv6 header and the payload.
+    pub fn calc_checksum_ipv6(
+        &self,
+        ip_header: &Ipv6Header,
+        payload: &[u8],
+    ) -> Result<u16, err::ValueTooBigError<usize>> {
+        self.calc_checksum_ipv6_raw(ip_header.source, ip_header.destination, payload)
+    }
+
+    /// Calculates the checksum of the current udp header given an ipv6 source & destination address plus the payload.
+    pub fn calc_checksum_ipv6_raw(
+        &self,
+        source: [u8; 16],
+        destination: [u8; 16],
+        payload: &[u8],
+    ) -> Result<u16, err::ValueTooBigError<usize>> {
+        //check that the total length fits into the field
+        const MAX_PAYLOAD_LENGTH: usize = (u32::MAX as usize) - UdpHeader::LEN;
+        if MAX_PAYLOAD_LENGTH < payload.len() {
+            return Err(err::ValueTooBigError {
+                actual: payload.len(),
+                max_allowed: MAX_PAYLOAD_LENGTH,
+                value_type: err::ValueType::UdpPayloadLengthIpv6,
+            });
+        }
+
+        Ok(self.calc_checksum_ipv6_internal(source, destination, payload))
+    }
+
+    fn calc_checksum_ipv6_internal(
+        &self,
+        source: [u8; 16],
+        destination: [u8; 16],
+        payload: &[u8],
+    ) -> u16 {
+        self.calc_checksum_post_ip(
+            //pseudo header
+            checksum::Sum16BitWords::new()
+                .add_16bytes(source)
+                .add_16bytes(destination)
+                .add_2bytes([0, ip_number::UDP.0])
+                .add_2bytes(self.length.to_be_bytes()),
+            payload,
+        )
+    }
+
+    /// This method takes the sum of the pseudo ip header and calculates the rest of the checksum.
+    fn calc_checksum_post_ip(
+        &self,
+        ip_pseudo_header_sum: checksum::Sum16BitWords,
+        payload: &[u8],
+    ) -> u16 {
+        ip_pseudo_header_sum
+            .add_2bytes(self.source_port.to_be_bytes())
+            .add_2bytes(self.destination_port.to_be_bytes())
+            .add_2bytes(self.length.to_be_bytes())
+            .add_slice(payload)
+            .to_ones_complement_with_no_zero()
+            .to_be()
+    }
+
+    /// Reads a udp header from a slice directly and returns a tuple containing the resulting header & unused part of the slice.
+    #[deprecated(since = "0.10.1", note = "Use UdpHeader::from_slice instead.")]
+    #[inline]
+    pub fn read_from_slice(slice: &[u8]) -> Result<(UdpHeader, &[u8]), err::LenError> {
+        UdpHeader::from_slice(slice)
+    }
+
+    /// Reads a udp header from a slice directly and returns a tuple containing the resulting header & unused part of the slice.
+    #[inline]
+    pub fn from_slice(slice: &[u8]) -> Result<(UdpHeader, &[u8]), err::LenError> {
+        Ok((
+            UdpHeaderSlice::from_slice(slice)?.to_header(),
+            &slice[UdpHeader::LEN..],
+        ))
+    }
+
+    /// Read an UdpHeader from a static sized byte array.
+    #[inline]
+    pub fn from_bytes(bytes: [u8; 8]) -> UdpHeader {
+        UdpHeader {
+            source_port: u16::from_be_bytes([bytes[0], bytes[1]]),
+            destination_port: u16::from_be_bytes([bytes[2], bytes[3]]),
+            length: u16::from_be_bytes([bytes[4], bytes[5]]),
+            checksum: u16::from_be_bytes([bytes[6], bytes[7]]),
+        }
+    }
+
+    /// Tries to read an udp header from the current position.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn read<T: std::io::Read + std::io::Seek + Sized>(
+        reader: &mut T,
+    ) -> Result<UdpHeader, std::io::Error> {
+        let bytes = {
+            let mut bytes: [u8; 8] = [0; 8];
+            reader.read_exact(&mut bytes)?;
+            bytes
+        };
+        Ok(UdpHeader::from_bytes(bytes))
+    }
+
+    /// Write the udp header without recalculating the checksum or length.
+    #[cfg(feature = "std")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+    pub fn write<T: std::io::Write + Sized>(&self, writer: &mut T) -> Result<(), std::io::Error> {
+        writer.write_all(&self.to_bytes())?;
+        Ok(())
+    }
+
+    /// Length of the serialized header in bytes.
+    ///
+    /// The function always returns the constant [`crate::UdpHeader::LEN`]
+    /// and exists to keep the methods consistent with other headers.
+    #[inline]
+    pub const fn header_len(&self) -> usize {
+        UdpHeader::LEN
+    }
+
+    /// Length of the serialized header in bytes in an [`u16`].
+    ///
+    /// The function always returns the constant [`crate::UdpHeader::LEN_U16`]
+    /// and exists to keep the methods consistent with other headers.
+    #[inline]
+    pub const fn header_len_u16(&self) -> u16 {
+        UdpHeader::LEN_U16
+    }
+
+    /// Returns the serialized form of the header as a statically
+    /// sized byte array.
+    #[inline]
+    pub fn to_bytes(&self) -> [u8; 8] {
+        let source_port_be = self.source_port.to_be_bytes();
+        let destination_port_be = self.destination_port.to_be_bytes();
+        let length_be = self.length.to_be_bytes();
+        let checksum = self.checksum.to_be_bytes();
+        [
+            source_port_be[0],
+            source_port_be[1],
+            destination_port_be[0],
+            destination_port_be[1],
+            length_be[0],
+            length_be[1],
+            checksum[0],
+            checksum[1],
+        ]
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{
+        err::{ValueTooBigError, ValueType},
+        test_gens::*,
+        *,
+    };
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+    use std::io::Cursor;
+
+    proptest! {
+        #[test]
+        fn without_ipv4_checksum(
+            source_port in any::<u16>(),
+            destination_port in any::<u16>(),
+            good_payload_length in 0..=((core::u16::MAX as usize) - UdpHeader::LEN),
+            bad_payload_length in ((core::u16::MAX as usize) - UdpHeader::LEN + 1)..=(isize::MAX as usize),
+        ) {
+
+            // normal working call
+            {
+                let actual = UdpHeader::without_ipv4_checksum(
+                    source_port,
+                    destination_port,
+                    good_payload_length
+                ).unwrap();
+                assert_eq!(
+                    actual,
+                    UdpHeader{
+                        source_port,
+                        destination_port,
+                        length: (UdpHeader::LEN + good_payload_length) as u16,
+                        checksum: 0
+                    }
+                );
+            }
+
+            // length too large
+            {
+                let actual = UdpHeader::without_ipv4_checksum(
+                    source_port,
+                    destination_port,
+                    bad_payload_length
+                ).unwrap_err();
+                assert_eq!(
+                    actual,
+                    ValueTooBigError{
+                        actual: bad_payload_length,
+                        max_allowed: usize::from(u16::MAX) - UdpHeader::LEN,
+                        value_type: err::ValueType::UdpPayloadLengthIpv4,
+                    }
+                );
+            }
+        }
+    }
+
+    /// Calculat the expected UDP header checksum for the tests.
+    fn expected_udp_ipv4_checksum(
+        source: [u8; 4],
+        destination: [u8; 4],
+        udp_header: &UdpHeader,
+        payload: &[u8],
+    ) -> u16 {
+        checksum::Sum16BitWords::new()
+            // pseudo header
+            .add_4bytes(source)
+            .add_4bytes(destination)
+            .add_2bytes([0, ip_number::UDP.0])
+            .add_2bytes(udp_header.length.to_be_bytes())
+            // udp header
+            .add_2bytes(udp_header.source_port.to_be_bytes())
+            .add_2bytes(udp_header.destination_port.to_be_bytes())
+            .add_2bytes(udp_header.length.to_be_bytes())
+            .add_2bytes([0, 0]) // checksum as zero (should have no effect)
+            .add_slice(payload)
+            .to_ones_complement_with_no_zero()
+            .to_be()
+    }
+
+    proptest! {
+        #[test]
+        fn with_ipv4_checksum(
+            source_port in any::<u16>(),
+            destination_port in any::<u16>(),
+            ipv4 in ipv4_any(),
+            payload in proptest::collection::vec(any::<u8>(), 0..20),
+            bad_len in ((core::u16::MAX as usize) - UdpHeader::LEN + 1)..=(isize::MAX as usize),
+        ) {
+            // normal case
+            assert_eq!(
+                UdpHeader::with_ipv4_checksum(
+                    source_port,
+                    destination_port,
+                    &ipv4,
+                    &payload
+                ).unwrap(),
+                {
+                    let mut expected = UdpHeader {
+                        source_port,
+                        destination_port,
+                        length: (UdpHeader::LEN + payload.len()) as u16,
+                        checksum: 0,
+                    };
+                    let checksum = expected_udp_ipv4_checksum(
+                        ipv4.source,
+                        ipv4.destination,
+                        &expected,
+                        &payload
+                    );
+                    expected.checksum = checksum;
+                    expected
+                }
+            );
+
+            // case where the 16 bit word results in a checksum of
+            // 0, but gets converted to 0xffff as 0 is reserved.
+            {
+                let base = UdpHeader {
+                    source_port: 0,
+                    destination_port,
+                    length: (UdpHeader::LEN + payload.len()) as u16,
+                    checksum: 0,
+                };
+                // use the source port to force 0 as a result value
+                // for that first calculate the checksum with the source
+                // set to 0
+                let sourceless_checksum = !(expected_udp_ipv4_checksum(
+                    ipv4.source,
+                    ipv4.destination,
+                    &base,
+                    &payload
+                ).to_le());
+
+                assert_eq!(
+                    UdpHeader::with_ipv4_checksum(
+                        // we now need to add a value that results in the value
+                        // 0xffff (which will become 0 via the ones complement rule).
+                        0xffff - sourceless_checksum,
+                        destination_port,
+                        &ipv4,
+                        &payload
+                    ).unwrap(),
+                    UdpHeader{
+                        source_port: 0xffff - sourceless_checksum,
+                        destination_port,
+                        length: base.length,
+                        checksum: 0xffff
+                    }
+                );
+            }
+
+            // length error case
+            {
+                // SAFETY: In case the error is not triggered
+                //         a segmentation fault will be triggered.
+                let too_big_slice = unsafe {
+                    //NOTE: The pointer must be initialized with a non null value
+                    //      otherwise a key constraint of slices is not fulfilled
+                    //      which can lead to crashes in release mode.
+                    use core::ptr::NonNull;
+                    core::slice::from_raw_parts(
+                        NonNull::<u8>::dangling().as_ptr(),
+                        bad_len
+                    )
+                };
+                assert_eq!(
+                    UdpHeader::with_ipv4_checksum(
+                        source_port,
+                        destination_port,
+                        &ipv4,
+                        &too_big_slice
+                    ).unwrap_err(),
+                    ValueTooBigError{
+                        actual: bad_len,
+                        max_allowed: usize::from(u16::MAX) - UdpHeader::LEN,
+                        value_type: err::ValueType::UdpPayloadLengthIpv4,
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn calc_checksum_ipv4_raw(
+            source_port in any::<u16>(),
+            destination_port in any::<u16>(),
+            dummy_checksum in any::<u16>(),
+            ipv4 in ipv4_any(),
+            payload in proptest::collection::vec(any::<u8>(), 0..20),
+            bad_len in ((core::u16::MAX as usize) - UdpHeader::LEN + 1)..=(isize::MAX as usize),
+        ) {
+            // normal case
+            {
+                let header = UdpHeader {
+                    source_port,
+                    destination_port,
+                    length: (UdpHeader::LEN + payload.len()) as u16,
+                    checksum: dummy_checksum,
+                };
+
+                assert_eq!(
+                    header.calc_checksum_ipv4_raw(
+                        ipv4.source,
+                        ipv4.destination,
+                        &payload
+                    ).unwrap(),
+                    expected_udp_ipv4_checksum(
+                        ipv4.source,
+                        ipv4.destination,
+                        &header,
+                        &payload
+                    )
+                );
+            }
+
+            // case where the 16 bit word results in a checksum of
+            // 0, but gets converted to 0xffff as 0 is reserved.
+            {
+                let base = UdpHeader {
+                    source_port: 0,
+                    destination_port,
+                    length: (UdpHeader::LEN + payload.len()) as u16,
+                    checksum: dummy_checksum,
+                };
+                // use the source port to force 0 as a result value
+                // for that first calculate the checksum with the source
+                // set to 0
+                let sourceless_checksum = !(expected_udp_ipv4_checksum(
+                    ipv4.source,
+                    ipv4.destination,
+                    &base,
+                    &payload
+                ).to_le());
+
+                // we now need to add a value that results in the value
+                // 0xffff (which will become 0 via the ones complement rule).
+                let header = {
+                    let mut header = base.clone();
+                    header.source_port = 0xffff - sourceless_checksum;
+                    header
+                };
+
+                assert_eq!(
+                    0xffff,
+                    header.calc_checksum_ipv4_raw(
+                        ipv4.source,
+                        ipv4.destination,
+                        &payload
+                    ).unwrap()
+                );
+            }
+
+            // length error case
+            {
+                let header = UdpHeader {
+                    source_port,
+                    destination_port,
+                    // udp header length itself is ok, but the payload not
+                    length: (UdpHeader::LEN + payload.len()) as u16,
+                    checksum: dummy_checksum,
+                };
+                // SAFETY: In case the error is not triggered
+                //         a segmentation fault will be triggered.
+                let too_big_slice = unsafe {
+                    //NOTE: The pointer must be initialized with a non null value
+                    //      otherwise a key constraint of slices is not fulfilled
+                    //      which can lead to crashes in release mode.
+                    use core::ptr::NonNull;
+                    core::slice::from_raw_parts(
+                        NonNull::<u8>::dangling().as_ptr(),
+                        bad_len
+                    )
+                };
+                assert_eq!(
+                    header.calc_checksum_ipv4_raw(
+                        ipv4.source,
+                        ipv4.destination,
+                        too_big_slice
+                    ).unwrap_err(),
+                    ValueTooBigError{
+                        actual: bad_len,
+                        max_allowed: (core::u16::MAX as usize) - UdpHeader::LEN,
+                        value_type: ValueType::UdpPayloadLengthIpv4,
+                    }
+                );
+            }
+        }
+    }
+
+    /// Calculat the expected UDP header checksum for the tests.
+    fn expected_udp_ipv6_checksum(
+        source: [u8; 16],
+        destination: [u8; 16],
+        udp_header: &UdpHeader,
+        payload: &[u8],
+    ) -> u16 {
+        checksum::Sum16BitWords::new()
+            // pseudo header
+            .add_16bytes(source)
+            .add_16bytes(destination)
+            .add_2bytes([0, ip_number::UDP.0])
+            .add_4bytes(u32::from(udp_header.length).to_be_bytes())
+            // udp header
+            .add_2bytes(udp_header.source_port.to_be_bytes())
+            .add_2bytes(udp_header.destination_port.to_be_bytes())
+            .add_2bytes(udp_header.length.to_be_bytes())
+            .add_2bytes([0, 0]) // checksum as zero (should have no effect)
+            .add_slice(payload)
+            .to_ones_complement_with_no_zero()
+            .to_be()
+    }
+
+    proptest! {
+        #[test]
+        fn with_ipv6_checksum(
+            source_port in any::<u16>(),
+            destination_port in any::<u16>(),
+            ipv6 in ipv6_any(),
+            payload in proptest::collection::vec(any::<u8>(), 0..20),
+            bad_len in ((core::u16::MAX as usize) - UdpHeader::LEN + 1)..=(isize::MAX as usize),
+        ) {
+            // normal case
+            assert_eq!(
+                UdpHeader::with_ipv6_checksum(
+                    source_port,
+                    destination_port,
+                    &ipv6,
+                    &payload
+                ).unwrap(),
+                {
+                    let mut expected = UdpHeader {
+                        source_port,
+                        destination_port,
+                        length: (UdpHeader::LEN + payload.len()) as u16,
+                        checksum: 0,
+                    };
+                    let checksum = expected_udp_ipv6_checksum(
+                        ipv6.source,
+                        ipv6.destination,
+                        &expected,
+                        &payload
+                    );
+                    expected.checksum = checksum;
+                    expected
+                }
+            );
+
+            // case where the 16 bit word results in a checksum of
+            // 0, but gets converted to 0xffff as 0 is reserved.
+            {
+                let base = UdpHeader {
+                    source_port: 0,
+                    destination_port,
+                    length: (UdpHeader::LEN + payload.len()) as u16,
+                    checksum: 0,
+                };
+                // use the source port to force 0 as a result value
+                // for that first calculate the checksum with the source
+                // set to 0
+                let sourceless_checksum = !(expected_udp_ipv6_checksum(
+                    ipv6.source,
+                    ipv6.destination,
+                    &base,
+                    &payload
+                ).to_le());
+
+                assert_eq!(
+                    UdpHeader::with_ipv6_checksum(
+                        // we now need to add a value that results in the value
+                        // 0xffff (which will become 0 via the ones complement rule).
+                        0xffff - sourceless_checksum,
+                        destination_port,
+                        &ipv6,
+                        &payload
+                    ).unwrap(),
+                    UdpHeader{
+                        source_port: 0xffff - sourceless_checksum,
+                        destination_port,
+                        length: base.length,
+                        checksum: 0xffff
+                    }
+                );
+            }
+
+            // length error case
+            {
+                // SAFETY: In case the error is not triggered
+                //         a segmentation fault will be triggered.
+                let too_big_slice = unsafe {
+                    //NOTE: The pointer must be initialized with a non null value
+                    //      otherwise a key constraint of slices is not fulfilled
+                    //      which can lead to crashes in release mode.
+                    use core::ptr::NonNull;
+                    core::slice::from_raw_parts(
+                        NonNull::<u8>::dangling().as_ptr(),
+                        bad_len
+                    )
+                };
+                assert_eq!(
+                    UdpHeader::with_ipv6_checksum(
+                        source_port,
+                        destination_port,
+                        &ipv6,
+                        &too_big_slice
+                    ).unwrap_err(),
+                    ValueTooBigError{
+                        actual: bad_len,
+                        max_allowed: usize::from(u16::MAX) - UdpHeader::LEN,
+                        value_type: err::ValueType::UdpPayloadLengthIpv6,
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
+        fn calc_checksum_ipv6(
+            source_port in any::<u16>(),
+            destination_port in any::<u16>(),
+            ipv6 in ipv6_any(),
+            payload in proptest::collection::vec(any::<u8>(), 0..20),
+            bad_len in ((core::u32::MAX as usize) - UdpHeader::LEN + 1)..=(isize::MAX as usize),
+        ) {
+            // normal case
+            assert_eq!(
+                UdpHeader::with_ipv6_checksum(
+                    source_port,
+                    destination_port,
+                    &ipv6,
+                    &payload
+                ).unwrap(),
+                {
+                    let mut expected = UdpHeader {
+                        source_port,
+                        destination_port,
+                        length: (UdpHeader::LEN + payload.len()) as u16,
+                        checksum: 0,
+                    };
+                    let checksum = expected_udp_ipv6_checksum(
+                        ipv6.source,
+                        ipv6.destination,
+                        &expected,
+                        &payload
+                    );
+                    expected.checksum = checksum;
+                    expected
+                }
+            );
+
+            // case where the 16 bit word results in a checksum of
+            // 0, but gets converted to 0xffff as 0 is reserved.
+            {
+                let base = UdpHeader {
+                    source_port: 0,
+                    destination_port,
+                    length: (UdpHeader::LEN + payload.len()) as u16,
+                    checksum: 0,
+                };
+                // use the source port to force 0 as a result value
+                // for that first calculate the checksum with the source
+                // set to 0
+                let sourceless_checksum = !(expected_udp_ipv6_checksum(
+                    ipv6.source,
+                    ipv6.destination,
+                    &base,
+                    &payload
+                ).to_le());
+
+                assert_eq!(
+                    UdpHeader::with_ipv6_checksum(
+                        // we now need to add a value that results in the value
+                        // 0xffff (which will become 0 via the ones complement rule).
+                        0xffff - sourceless_checksum,
+                        destination_port,
+                        &ipv6,
+                        &payload
+                    ).unwrap(),
+                    UdpHeader{
+                        source_port: 0xffff - sourceless_checksum,
+                        destination_port,
+                        length: base.length,
+                        checksum: 0xffff
+                    }
+                );
+            }
+
+            // length error case
+            {
+                // SAFETY: In case the error is not triggered
+                //         a segmentation fault will be triggered.
+                let too_big_slice = unsafe {
+                    //NOTE: The pointer must be initialized with a non null value
+                    //      otherwise a key constraint of slices is not fulfilled
+                    //      which can lead to crashes in release mode.
+                    use core::ptr::NonNull;
+                    core::slice::from_raw_parts(
+                        NonNull::<u8>::dangling().as_ptr(),
+                        bad_len
+                    )
+                };
+                assert_eq!(
+                    UdpHeader::with_ipv6_checksum(
+                        source_port,
+                        destination_port,
+                        &ipv6,
+                        &too_big_slice
+                    ).unwrap_err(),
+                    ValueTooBigError{
+                        actual: bad_len,
+                        max_allowed: usize::from(u16::MAX) - UdpHeader::LEN,
+                        value_type: err::ValueType::UdpPayloadLengthIpv6,
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
+        fn calc_checksum_ipv6_raw(
+            source_port in any::<u16>(),
+            destination_port in any::<u16>(),
+            dummy_checksum in any::<u16>(),
+            ipv6 in ipv6_any(),
+            payload in proptest::collection::vec(any::<u8>(), 0..20),
+            bad_len in ((core::u32::MAX as usize) - UdpHeader::LEN + 1)..=(isize::MAX as usize),
+        ) {
+            // normal case
+            {
+                let header = UdpHeader {
+                    source_port,
+                    destination_port,
+                    length: (UdpHeader::LEN + payload.len()) as u16,
+                    checksum: dummy_checksum,
+                };
+
+                assert_eq!(
+                    header.calc_checksum_ipv6_raw(
+                        ipv6.source,
+                        ipv6.destination,
+                        &payload
+                    ).unwrap(),
+                    expected_udp_ipv6_checksum(
+                        ipv6.source,
+                        ipv6.destination,
+                        &header,
+                        &payload
+                    )
+                );
+            }
+
+            // case where the 16 bit word results in a checksum of
+            // 0, but gets converted to 0xffff as 0 is reserved.
+            {
+                let base = UdpHeader {
+                    source_port: 0,
+                    destination_port,
+                    length: (UdpHeader::LEN + payload.len()) as u16,
+                    checksum: dummy_checksum,
+                };
+                // use the source port to force 0 as a result value
+                // for that first calculate the checksum with the source
+                // set to 0
+                let sourceless_checksum = !(expected_udp_ipv6_checksum(
+                    ipv6.source,
+                    ipv6.destination,
+                    &base,
+                    &payload
+                ).to_le());
+
+                // we now need to add a value that results in the value
+                // 0xffff (which will become 0 via the ones complement rule).
+                let header = {
+                    let mut header = base.clone();
+                    header.source_port = 0xffff - sourceless_checksum;
+                    header
+                };
+
+                assert_eq!(
+                    0xffff,
+                    header.calc_checksum_ipv6_raw(
+                        ipv6.source,
+                        ipv6.destination,
+                        &payload
+                    ).unwrap()
+                );
+            }
+
+            // length error case
+            {
+                let header = UdpHeader {
+                    source_port,
+                    destination_port,
+                    // udp header length itself is ok, but the payload not
+                    length: (UdpHeader::LEN + payload.len()) as u16,
+                    checksum: dummy_checksum,
+                };
+                // SAFETY: In case the error is not triggered
+                //         a segmentation fault will be triggered.
+                let too_big_slice = unsafe {
+                    //NOTE: The pointer must be initialized with a non null value
+                    //      otherwise a key constraint of slices is not fulfilled
+                    //      which can lead to crashes in release mode.
+                    use core::ptr::NonNull;
+                    core::slice::from_raw_parts(
+                        NonNull::<u8>::dangling().as_ptr(),
+                        bad_len
+                    )
+                };
+                assert_eq!(
+                    header.calc_checksum_ipv6_raw(
+                        ipv6.source,
+                        ipv6.destination,
+                        too_big_slice
+                    ).unwrap_err(),
+                    ValueTooBigError{
+                        actual: bad_len,
+                        max_allowed: (core::u32::MAX as usize) - UdpHeader::LEN,
+                        value_type: ValueType::UdpPayloadLengthIpv6,
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            input in udp_any(),
+            dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
+        ) {
+            // serialize
+            let mut buffer: Vec<u8> = Vec::with_capacity(8 + dummy_data.len());
+            input.write(&mut buffer).unwrap();
+            buffer.extend(&dummy_data[..]);
+
+            // calls with a valid result
+            {
+                let (result, rest) = UdpHeader::from_slice(&buffer[..]).unwrap();
+                assert_eq!(result, input);
+                assert_eq!(rest, &buffer[8..]);
+            }
+            #[allow(deprecated)]
+            {
+                let (result, rest) = UdpHeader::read_from_slice(&buffer[..]).unwrap();
+                assert_eq!(result, input);
+                assert_eq!(rest, &buffer[8..]);
+            }
+
+            // call with not enough data in the slice
+            for len in 0..8 {
+                assert_eq!(
+                    UdpHeader::from_slice(&buffer[0..len]).unwrap_err(),
+                    err::LenError{
+                        required_len: UdpHeader::LEN,
+                        len: len,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::UdpHeader,
+                        layer_start_offset: 0,
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_bytes(input in udp_any()) {
+            assert_eq!(
+                input,
+                UdpHeader::from_bytes(
+                    input.to_bytes()
+                )
+            );
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn read(
+            input in udp_any(),
+            dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
+        ) {
+            // serialize
+            let mut buffer: Vec<u8> = 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 = UdpHeader::read(&mut cursor).unwrap();
+                assert_eq!(result, input);
+                assert_eq!(8, cursor.position());
+            }
+
+            // unexpexted eof
+            for len in 0..8 {
+                let mut cursor = Cursor::new(&buffer[0..len]);
+                assert!(
+                    UdpHeader::read(&mut cursor)
+                    .is_err()
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn write(input in udp_any()) {
+            // normal write
+            {
+                let mut result = Vec::with_capacity(input.header_len());
+                input.write(&mut result).unwrap();
+                assert_eq!(
+                    &result[..],
+                    input.to_bytes()
+                );
+            }
+
+            // unexpected eof
+            for len in 0..8 {
+                let mut buffer = [0u8; 8];
+                let mut cursor = Cursor::new(&mut buffer[..len]);
+                assert!(
+                    input.write(&mut cursor)
+                        .is_err()
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_bytes(input in udp_any()) {
+            let s_be = input.source_port.to_be_bytes();
+            let d_be = input.destination_port.to_be_bytes();
+            let l_be = input.length.to_be_bytes();
+            let c_be = input.checksum.to_be_bytes();
+
+            assert_eq!(
+                input.to_bytes(),
+                [
+                    s_be[0],
+                    s_be[1],
+                    d_be[0],
+                    d_be[1],
+                    l_be[0],
+                    l_be[1],
+                    c_be[0],
+                    c_be[1],
+                ]
+            );
+        }
+    }
+
+    #[test]
+    fn default() {
+        let actual: UdpHeader = Default::default();
+        assert_eq!(actual.source_port, 0);
+        assert_eq!(actual.destination_port, 0);
+        assert_eq!(actual.length, 0);
+        assert_eq!(actual.checksum, 0);
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(input in udp_any()) {
+            assert_eq!(input, input.clone());
+            {
+                let mut other = input.clone();
+                other.source_port = !input.source_port;
+                assert!(input != other);
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn dbg(input in udp_any()) {
+            assert_eq!(
+                &format!(
+                    "UdpHeader {{ source_port: {}, destination_port: {}, length: {}, checksum: {} }}",
+                    input.source_port,
+                    input.destination_port,
+                    input.length,
+                    input.checksum,
+                ),
+                &format!("{:?}", input)
+            );
+        }
+    }
+}
diff --git a/src/transport/udp_header_slice.rs b/src/transport/udp_header_slice.rs
new file mode 100644
index 0000000..d9823c2
--- /dev/null
+++ b/src/transport/udp_header_slice.rs
@@ -0,0 +1,172 @@
+use crate::*;
+use core::slice::from_raw_parts;
+
+///A slice containing an udp header of a network package. Struct allows the selective read of fields in the header.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct UdpHeaderSlice<'a> {
+    pub(crate) slice: &'a [u8],
+}
+
+impl<'a> UdpHeaderSlice<'a> {
+    /// Creates a slice containing an udp header.
+    #[inline]
+    pub fn from_slice(slice: &'a [u8]) -> Result<UdpHeaderSlice<'a>, err::LenError> {
+        //check length
+        if slice.len() < UdpHeader::LEN {
+            return Err(err::LenError {
+                required_len: UdpHeader::LEN,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: err::Layer::UdpHeader,
+                layer_start_offset: 0,
+            });
+        }
+
+        //done
+        Ok(UdpHeaderSlice {
+            // SAFETY:
+            // Safe as slice length is checked to be at least
+            // UdpHeader::LEN (8) before this.
+            slice: unsafe { from_raw_parts(slice.as_ptr(), UdpHeader::LEN) },
+        })
+    }
+
+    /// Returns the slice containing the udp header
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+
+    /// Reads the "udp source port" from the slice.
+    #[inline]
+    pub fn source_port(&self) -> u16 {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of UdpHeader::LEN (8).
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr()) }
+    }
+
+    /// Reads the "udp destination port" from the slice.
+    #[inline]
+    pub fn destination_port(&self) -> u16 {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of UdpHeader::LEN (8).
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) }
+    }
+
+    /// Reads the "length" from the slice.
+    #[inline]
+    pub fn length(&self) -> u16 {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of UdpHeader::LEN (8).
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(4)) }
+    }
+
+    /// Reads the "checksum" from the slice.
+    #[inline]
+    pub fn checksum(&self) -> u16 {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of UdpHeader::LEN (8).
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(6)) }
+    }
+
+    /// Decode all the fields and copy the results to a UdpHeader struct
+    #[inline]
+    pub fn to_header(&self) -> UdpHeader {
+        UdpHeader {
+            source_port: self.source_port(),
+            destination_port: self.destination_port(),
+            length: self.length(),
+            checksum: self.checksum(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use crate::{test_gens::*, *};
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            input in udp_any(),
+            dummy_data in proptest::collection::vec(any::<u8>(), 0..20)
+        ) {
+            // serialize
+            let mut buffer: Vec<u8> = Vec::with_capacity(8 + dummy_data.len());
+            input.write(&mut buffer).unwrap();
+            buffer.extend(&dummy_data[..]);
+
+            // calls with a valid result
+            {
+                let result = UdpHeaderSlice::from_slice(&buffer[..]).unwrap();
+                assert_eq!(&buffer[..8], result.slice());
+            }
+
+            // call with not enough data in the slice
+            for len in 0..8 {
+                assert_eq!(
+                    UdpHeaderSlice::from_slice(&buffer[0..len]).unwrap_err(),
+                    err::LenError{
+                        required_len: UdpHeader::LEN,
+                        len: len,
+                        len_source: LenSource::Slice,
+                        layer: err::Layer::UdpHeader,
+                        layer_start_offset: 0,
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn getters(input in udp_any()) {
+            let bytes = input.to_bytes();
+            let slice = UdpHeaderSlice::from_slice(&bytes).unwrap();
+
+            assert_eq!(slice.source_port(), input.source_port);
+            assert_eq!(slice.destination_port(), input.destination_port);
+            assert_eq!(slice.length(), input.length);
+            assert_eq!(slice.checksum(), input.checksum);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn to_header(input in udp_any()) {
+            let bytes = input.to_bytes();
+            let slice = UdpHeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(input, slice.to_header());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn clone_eq(input in udp_any()) {
+            let bytes = input.to_bytes();
+            let slice = UdpHeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(slice, slice.clone());
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn dbg(input in udp_any()) {
+            let bytes = input.to_bytes();
+            let slice = UdpHeaderSlice::from_slice(&bytes).unwrap();
+            assert_eq!(
+                &format!(
+                    "UdpHeaderSlice {{ slice: {:?} }}",
+                    slice.slice()
+                ),
+                &format!("{:?}", slice)
+            );
+        }
+    }
+}
diff --git a/src/transport/udp_slice.rs b/src/transport/udp_slice.rs
new file mode 100644
index 0000000..28937f9
--- /dev/null
+++ b/src/transport/udp_slice.rs
@@ -0,0 +1,459 @@
+use crate::{err::*, *};
+
+/// Slice containing the UDP headers & payload.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct UdpSlice<'a> {
+    slice: &'a [u8],
+}
+
+impl<'a> UdpSlice<'a> {
+    /// Decode length from UDP header and restrict slice to the length
+    /// of the header including the payload.
+    ///
+    /// Note that this method fall backs to the length of the slice
+    /// in the case the length field in the UDP header is set to zero.
+    pub fn from_slice(slice: &'a [u8]) -> Result<UdpSlice<'a>, LenError> {
+        // slice header
+        let header = UdpHeaderSlice::from_slice(slice)?;
+
+        // validate the length of the slice
+        let len: usize = header.length().into();
+        if slice.len() < len {
+            return Err(LenError {
+                required_len: len,
+                len: slice.len(),
+                len_source: LenSource::Slice,
+                layer: Layer::UdpPayload,
+                layer_start_offset: 0,
+            });
+        }
+
+        // fallback to the slice length in case length is set to 0
+        if len == 0 {
+            Ok(UdpSlice { slice })
+        } else {
+            // validate the length
+            if len < UdpHeader::LEN {
+                // TODO: Should this replaced with a custom error?
+                Err(LenError {
+                    required_len: UdpHeader::LEN,
+                    len,
+                    len_source: LenSource::UdpHeaderLen,
+                    layer: Layer::UdpHeader,
+                    layer_start_offset: 0,
+                })
+            } else {
+                Ok(UdpSlice {
+                    // SAFETY: Safe as slice.len() was validated before to
+                    // be at least as big as "len".
+                    slice: unsafe { core::slice::from_raw_parts(slice.as_ptr(), len) },
+                })
+            }
+        }
+    }
+
+    /// Try decoding length from UDP header and restrict slice to the length
+    /// of the header including the payload if possible. If not the slice length
+    /// is used as a fallback value.
+    ///
+    /// Note that this method fall also backs to the length of the slice
+    /// in the case the length field in the UDP header is set to zero or smaller
+    /// then the minimum header length.
+    pub fn from_slice_lax(slice: &'a [u8]) -> Result<UdpSlice<'a>, LenError> {
+        // slice header
+        let header = UdpHeaderSlice::from_slice(slice)?;
+
+        // validate the length of the slice and fallback to the slice
+        // length if the slice is smaller then expected or zero.
+        let len: usize = header.length().into();
+        if slice.len() < len || len < UdpHeader::LEN {
+            Ok(UdpSlice { slice })
+        } else {
+            Ok(UdpSlice {
+                // SAFETY: Safe as slice.len() was validated before to
+                // be at least as big as "len".
+                slice: unsafe { core::slice::from_raw_parts(slice.as_ptr(), len) },
+            })
+        }
+    }
+
+    /// Return the slice containing the UDP header & payload.
+    #[inline]
+    pub fn slice(&self) -> &'a [u8] {
+        self.slice
+    }
+
+    /// Return the slice containing the UDP header.
+    #[inline]
+    pub fn header_slice(&self) -> &'a [u8] {
+        unsafe {
+            // SAFETY: Safe as the slice length was verified
+            // to be at least UdpHeader::LEN by "from_slice".
+            core::slice::from_raw_parts(self.slice.as_ptr(), UdpHeader::LEN)
+        }
+    }
+
+    /// Returns the slice containing the UDP payload.
+    #[inline]
+    pub fn payload(&self) -> &'a [u8] {
+        unsafe {
+            // SAFETY: Safe as the slice length was verified
+            // to be at least UdpHeader::LEN by "from_slice".
+            core::slice::from_raw_parts(
+                self.slice.as_ptr().add(UdpHeader::LEN),
+                self.slice.len() - UdpHeader::LEN,
+            )
+        }
+    }
+
+    /// Value that was used to determine the length of the payload.
+    #[inline]
+    pub fn payload_len_source(&self) -> LenSource {
+        if usize::from(self.length()) == self.slice.len() {
+            LenSource::UdpHeaderLen
+        } else {
+            LenSource::Slice
+        }
+    }
+
+    /// Reads the "udp source port" in the UDP header.
+    #[inline]
+    pub fn source_port(&self) -> u16 {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of UdpHeader::LEN (8).
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr()) }
+    }
+
+    /// Reads the "udp destination port" in the UDP header.
+    #[inline]
+    pub fn destination_port(&self) -> u16 {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of UdpHeader::LEN (8).
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) }
+    }
+
+    /// Reads the "length" field in the UDP header.
+    #[inline]
+    pub fn length(&self) -> u16 {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of UdpHeader::LEN (8).
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(4)) }
+    }
+
+    /// Reads the "checksum" from the slice.
+    #[inline]
+    pub fn checksum(&self) -> u16 {
+        // SAFETY:
+        // Safe as the contructor checks that the slice has
+        // at least the length of UdpHeader::LEN (8).
+        unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(6)) }
+    }
+
+    /// Length of the UDP header (equal to [`crate::UdpHeader::LEN`]).
+    #[inline]
+    pub const fn header_len(&self) -> usize {
+        UdpHeader::LEN
+    }
+
+    /// Length of the UDP header in an [`u16`] (equal to [`crate::UdpHeader::LEN_U16`]).
+    #[inline]
+    pub const fn header_len_u16(&self) -> u16 {
+        UdpHeader::LEN_U16
+    }
+
+    /// Decode all the fields of the UDP header and copy the results
+    /// to a UdpHeader struct.
+    #[inline]
+    pub fn to_header(&self) -> UdpHeader {
+        UdpHeader {
+            source_port: self.source_port(),
+            destination_port: self.destination_port(),
+            length: self.length(),
+            checksum: self.checksum(),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_gens::*;
+    use alloc::{format, vec::Vec};
+    use proptest::prelude::*;
+
+    proptest! {
+        #[test]
+        fn debug_clone_eq(
+            udp_base in udp_any()
+        ) {
+            let payload: [u8;4] = [1,2,3,4];
+            let mut data = Vec::with_capacity(
+                udp_base.header_len() +
+                payload.len()
+            );
+            let mut udp = udp_base.clone();
+            udp.length = (UdpHeader::LEN + payload.len()) as u16;
+            data.extend_from_slice(&udp.to_bytes());
+            data.extend_from_slice(&payload);
+
+            // decode packet
+            let slice = UdpSlice::from_slice(&data).unwrap();
+
+            // check debug output
+            prop_assert_eq!(
+                format!("{:?}", slice),
+                format!(
+                    "UdpSlice {{ slice: {:?} }}",
+                    &data[..]
+                )
+            );
+            prop_assert_eq!(slice.clone(), slice);
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn getters(
+            udp_base in udp_any()
+        ) {
+            let udp = {
+                let mut udp = udp_base.clone();
+                udp.length = UdpHeader::LEN as u16;
+                udp
+            };
+            let data = {
+                let mut data = Vec::with_capacity(
+                    udp.header_len()
+                );
+                data.extend_from_slice(&udp.to_bytes());
+                data
+            };
+
+            // normal decode
+            {
+                let slice = UdpSlice::from_slice(&data).unwrap();
+                assert_eq!(slice.slice(), &data);
+                assert_eq!(slice.header_slice(), &data);
+                assert_eq!(slice.payload(), &[]);
+                assert_eq!(slice.source_port(), udp.source_port);
+                assert_eq!(slice.destination_port(), udp.destination_port);
+                assert_eq!(slice.length(), udp.length);
+                assert_eq!(slice.checksum(), udp.checksum);
+                assert_eq!(slice.to_header(), udp);
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice(
+            udp_base in udp_any()
+        ) {
+            let payload: [u8;4] = [1,2,3,4];
+            let udp = {
+                let mut udp = udp_base.clone();
+                udp.length = (UdpHeader::LEN + payload.len()) as u16;
+                udp
+            };
+            let data = {
+                let mut data = Vec::with_capacity(
+                    udp.header_len() +
+                    payload.len()
+                );
+                data.extend_from_slice(&udp.to_bytes());
+                data.extend_from_slice(&payload);
+                data
+            };
+
+            // normal decode
+            {
+                let slice = UdpSlice::from_slice(&data).unwrap();
+                assert_eq!(udp, slice.to_header());
+                assert_eq!(payload, slice.payload());
+            }
+
+            // decode a payload smaller then the given slice
+            {
+                let mut mod_data = data.clone();
+                let reduced_len = (UdpHeader::LEN + payload.len() - 1) as u16;
+                // inject the reduced length
+                {
+                    let rl_be = reduced_len.to_be_bytes();
+                    mod_data[4] = rl_be[0];
+                    mod_data[5] = rl_be[1];
+                }
+
+                let slice = UdpSlice::from_slice(&mod_data).unwrap();
+                assert_eq!(
+                    slice.to_header(),
+                    {
+                        let mut expected = slice.to_header();
+                        expected.length = reduced_len;
+                        expected
+                    }
+                );
+                assert_eq!(&payload[..payload.len() - 1], slice.payload());
+            }
+
+            // if length is zero the length given by the slice should be used
+            {
+                // inject zero as length
+                let mut mod_data = data.clone();
+                mod_data[4] = 0;
+                mod_data[5] = 0;
+
+                let slice = UdpSlice::from_slice(&mod_data).unwrap();
+
+                assert_eq!(slice.source_port(), udp_base.source_port);
+                assert_eq!(slice.destination_port(), udp_base.destination_port);
+                assert_eq!(slice.checksum(), udp_base.checksum);
+                assert_eq!(slice.length(), 0);
+                assert_eq!(&payload, slice.payload());
+            }
+
+            // too little data to even decode the header
+            for len in 0..UdpHeader::LEN {
+                assert_eq!(
+                    UdpSlice::from_slice(&data[..len]).unwrap_err(),
+                    LenError {
+                        required_len: UdpHeader::LEN,
+                        len,
+                        len_source: LenSource::Slice,
+                        layer: Layer::UdpHeader,
+                        layer_start_offset: 0,
+                    }
+                );
+            }
+
+            // slice length smaller then the length described in the header
+            assert_eq!(
+                UdpSlice::from_slice(&data[..data.len() - 1]).unwrap_err(),
+                LenError {
+                    required_len: data.len(),
+                    len: data.len() - 1,
+                    len_source: LenSource::Slice,
+                    layer: Layer::UdpPayload,
+                    layer_start_offset: 0,
+                }
+            );
+
+            // length in header smaller than the header itself
+            {
+                let mut mod_data = data.clone();
+                // inject the reduced length
+                {
+                    let len_be = ((UdpHeader::LEN - 1) as u16).to_be_bytes();
+                    mod_data[4] = len_be[0];
+                    mod_data[5] = len_be[1];
+                }
+                assert_eq!(
+                    UdpSlice::from_slice(&mod_data).unwrap_err(),
+                    LenError {
+                        required_len: UdpHeader::LEN,
+                        len: UdpHeader::LEN - 1,
+                        len_source: LenSource::UdpHeaderLen,
+                        layer: Layer::UdpHeader,
+                        layer_start_offset: 0
+                    }
+                );
+            }
+        }
+    }
+
+    proptest! {
+        #[test]
+        fn from_slice_lax(
+            udp_base in udp_any()
+        ) {
+            let payload: [u8;4] = [1,2,3,4];
+            let udp = {
+                let mut udp = udp_base.clone();
+                udp.length = (UdpHeader::LEN + payload.len()) as u16;
+                udp
+            };
+            let data = {
+                let mut data = Vec::with_capacity(
+                    udp.header_len() +
+                    payload.len()
+                );
+                data.extend_from_slice(&udp.to_bytes());
+                data.extend_from_slice(&payload);
+                data
+            };
+
+            // normal decode
+            {
+                let slice = UdpSlice::from_slice_lax(&data).unwrap();
+                assert_eq!(udp, slice.to_header());
+                assert_eq!(payload, slice.payload());
+                assert_eq!(slice.payload_len_source(), LenSource::UdpHeaderLen);
+            }
+
+            // decode a payload smaller then the given slice
+            {
+                let mut mod_data = data.clone();
+                let reduced_len = (UdpHeader::LEN + payload.len() - 1) as u16;
+                // inject the reduced length
+                {
+                    let rl_be = reduced_len.to_be_bytes();
+                    mod_data[4] = rl_be[0];
+                    mod_data[5] = rl_be[1];
+                }
+
+                let slice = UdpSlice::from_slice_lax(&mod_data).unwrap();
+                assert_eq!(
+                    slice.to_header(),
+                    {
+                        let mut expected = slice.to_header();
+                        expected.length = reduced_len;
+                        expected
+                    }
+                );
+                assert_eq!(&payload[..payload.len() - 1], slice.payload());
+                assert_eq!(slice.payload_len_source(), LenSource::UdpHeaderLen);
+            }
+
+            // if length is zero the length given by the slice should be used
+            for len in 0..UdpHeader::LEN_U16{
+                // inject zero as length
+                let mut mod_data = data.clone();
+                mod_data[4] = len.to_be_bytes()[0];
+                mod_data[5] = len.to_be_bytes()[1];
+
+                let slice = UdpSlice::from_slice_lax(&mod_data).unwrap();
+
+                assert_eq!(slice.source_port(), udp_base.source_port);
+                assert_eq!(slice.destination_port(), udp_base.destination_port);
+                assert_eq!(slice.checksum(), udp_base.checksum);
+                assert_eq!(slice.length(), len);
+                assert_eq!(&payload, slice.payload());
+                assert_eq!(slice.payload_len_source(), LenSource::Slice);
+            }
+
+            // too little data to even decode the header
+            for len in 0..UdpHeader::LEN {
+                assert_eq!(
+                    UdpSlice::from_slice_lax(&data[..len]).unwrap_err(),
+                    LenError {
+                        required_len: UdpHeader::LEN,
+                        len,
+                        len_source: LenSource::Slice,
+                        layer: Layer::UdpHeader,
+                        layer_start_offset: 0,
+                    }
+                );
+            }
+
+            // slice length smaller then the length described in the header
+            {
+                let slice = UdpSlice::from_slice_lax(&data[..data.len() - 1]).unwrap();
+                assert_eq!(udp, slice.to_header());
+                assert_eq!(&payload[..payload.len() - 1], slice.payload());
+                assert_eq!(slice.payload_len_source(), LenSource::Slice);
+            }
+        }
+    }
+}
diff --git a/tests/transport/icmpv4.rs b/tests/transport/icmpv4.rs
new file mode 100644
index 0000000..c86a8c1
--- /dev/null
+++ b/tests/transport/icmpv4.rs
@@ -0,0 +1,204 @@
+use super::super::*;
+
+mod icmpv4_regression {
+    use super::*;
+
+    #[test]
+    fn icmp4_echo_marshall_unmarshall() {
+        let icmp4 = Icmpv4Header {
+            icmp_type: Icmpv4Type::EchoRequest(IcmpEchoHeader { seq: 1, id: 2 }),
+            checksum: 0,
+        };
+        // serialize
+        let mut buffer: Vec<u8> = Vec::with_capacity(256);
+        icmp4.write(&mut buffer).unwrap();
+        let (new_icmp4, rest) = Icmpv4Header::from_slice(&buffer).unwrap();
+        assert_eq!(icmp4, new_icmp4);
+        assert_eq!(rest.len(), 0);
+    }
+
+    #[test]
+    fn ip4_echo_marshall_unmarshall() {
+        let builder = PacketBuilder::ipv4(
+            [192, 168, 1, 1], //source ip
+            [192, 168, 1, 2], //destination ip
+            20,
+        ) //time to life
+        .icmpv4_echo_request(1, 2);
+        let payload = [0xde, 0xad, 0xbe, 0xef];
+        //get some memory to store the result
+        let mut result = Vec::<u8>::with_capacity(builder.size(payload.len()));
+
+        //serialize
+        builder.write(&mut result, &payload).unwrap();
+
+        let new_ip = PacketHeaders::from_ip_slice(&result).unwrap();
+        if let Some(TransportHeader::Icmpv4(hdr)) = new_ip.transport {
+            if let Icmpv4Type::EchoRequest(echo) = hdr.icmp_type {
+                assert_eq!(echo.id, 1);
+                assert_eq!(echo.seq, 2);
+            } else {
+                panic!("Not an EchoRequest!?");
+            }
+        } else {
+            panic!("No transport header found!?")
+        }
+    }
+    const ICMP4_ECHO_REQUEST_BYTES: [u8; 98] = [
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45,
+        0x00, 0x00, 0x54, 0x13, 0x6f, 0x40, 0x00, 0x40, 0x01, 0x29, 0x38, 0x7f, 0x00, 0x00, 0x01,
+        0x7f, 0x00, 0x00, 0x01, 0x08, 0x00, 0xc9, 0x99, 0x00, 0x03, 0x00, 0x01, 0x79, 0xc5, 0xd9,
+        0x61, 0x00, 0x00, 0x00, 0x00, 0x18, 0x68, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11,
+        0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+        0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+    ];
+
+    const ICMP4_ECHO_REPLY_BYTES: [u8; 98] = [
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45,
+        0x00, 0x00, 0x54, 0x13, 0x70, 0x00, 0x00, 0x40, 0x01, 0x69, 0x37, 0x7f, 0x00, 0x00, 0x01,
+        0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0xd1, 0x99, 0x00, 0x03, 0x00, 0x01, 0x79, 0xc5, 0xd9,
+        0x61, 0x00, 0x00, 0x00, 0x00, 0x18, 0x68, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11,
+        0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+        0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+        0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+    ];
+
+    // real echo request/reply captured from tcpdump
+    // ping 127.0.0.1 to 127.0.0.1
+    #[test]
+    fn pcap_echo_session() {
+        let request = PacketHeaders::from_ethernet_slice(&ICMP4_ECHO_REQUEST_BYTES).unwrap();
+        let request_icmp4 = request.transport.unwrap().icmpv4().unwrap();
+        match request_icmp4.icmp_type {
+            Icmpv4Type::EchoRequest(echo) => {
+                assert_eq!(echo.seq, 1);
+                assert_eq!(echo.id, 3); // arbitrarily assigned by OS
+            }
+            _ => panic!(r#"Request didn't parse as ICMP4!?"#),
+        }
+
+        let reply = PacketHeaders::from_ethernet_slice(&ICMP4_ECHO_REPLY_BYTES).unwrap();
+        let reply_icmp4 = reply.transport.unwrap().icmpv4().unwrap();
+        match reply_icmp4.icmp_type {
+            Icmpv4Type::EchoReply(echo) => {
+                assert_eq!(echo.seq, 1);
+                assert_eq!(echo.id, 3); // arbitrarily assigned by OS
+            }
+            _ => panic!(r#"Request didn't parse as ICMP4!?"#),
+        }
+        let request_iph = request.net.unwrap();
+        let reply_iph = reply.net.unwrap();
+        if let NetHeaders::Ipv4(request_ip, _) = request_iph {
+            if let NetHeaders::Ipv4(reply_ip, _) = reply_iph {
+                assert_eq!(reply_ip.source, request_ip.destination);
+                assert_eq!(reply_ip.destination, request_ip.source);
+            } else {
+                panic!("reply ip not v4!?");
+            }
+        } else {
+            panic!("request ip not v4!?");
+        }
+    }
+
+    #[test]
+    fn echo_request_slice() {
+        let echo = SlicedPacket::from_ethernet(&ICMP4_ECHO_REQUEST_BYTES).unwrap();
+        use TransportSlice::*;
+        let icmp4 = match echo.transport.unwrap() {
+            Icmpv4(icmp4) => icmp4,
+            Icmpv6(_) | Udp(_) | Tcp(_) => panic!("Misparsed header!"),
+        };
+        assert!(matches!(icmp4.icmp_type(), Icmpv4Type::EchoRequest(_)));
+    }
+
+    #[test]
+    fn verify_icmp4_checksum() {
+        for (pkt, checksum) in [
+            (ICMP4_ECHO_REQUEST_BYTES, 0xc999),
+            (ICMP4_ECHO_REPLY_BYTES, 0xd199),
+        ] {
+            // make sure we can unmarshall the correct checksum
+            let request = PacketHeaders::from_ethernet_slice(&pkt).unwrap();
+            let mut icmp4 = request.transport.unwrap().icmpv4().unwrap();
+            let valid_checksum = icmp4.checksum;
+            assert_ne!(valid_checksum, 0);
+            assert_eq!(valid_checksum, checksum);
+            // reset it and recalculate
+            icmp4.checksum = 0;
+            assert_eq!(
+                icmp4.icmp_type.calc_checksum(request.payload.slice()),
+                valid_checksum
+            );
+        }
+    }
+
+    // TTL unreachable from 'traceroute google.com'
+    const ICMP4_TTL_EXCEEDED_BYTES: [u8; 94] = [
+        0x98, 0x8d, 0x46, 0xc5, 0x03, 0x82, 0x60, 0xa4, 0xb7, 0x25, 0x4b, 0x84, 0x08, 0x00, 0x45,
+        0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x5c, 0x87, 0xd4, 0x9c, 0xc9, 0x72,
+        0xc0, 0xa8, 0x01, 0x6e, 0x0b, 0x00, 0x24, 0x29, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00,
+        0x3c, 0xe3, 0xaf, 0x00, 0x00, 0x01, 0x11, 0x14, 0x84, 0xc0, 0xa8, 0x01, 0x6e, 0xd8, 0xef,
+        0x26, 0x78, 0xc2, 0x8e, 0x82, 0x9f, 0x00, 0x28, 0x03, 0xed, 0x40, 0x41, 0x42, 0x43, 0x44,
+        0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53,
+        0x54, 0x55, 0x56, 0x57,
+    ];
+    #[test]
+    fn parse_icmp4_ttl_exceeded() {
+        let ttl_exceeded = PacketHeaders::from_ethernet_slice(&ICMP4_TTL_EXCEEDED_BYTES).unwrap();
+        let ip_header = match ttl_exceeded.net.unwrap() {
+            NetHeaders::Ipv4(ip4, _) => ip4,
+            _ => panic!("Didn't parse inner v4 IP header!?"),
+        };
+        assert_eq!(ip_header.source, [212, 156, 201, 114]);
+        let icmp4 = ttl_exceeded.transport.unwrap().icmpv4().unwrap();
+        let icmp_bytes = icmp4.to_bytes();
+        assert_eq!(8, icmp_bytes.len());
+        assert_eq!(icmp_bytes[0], icmpv4::TYPE_TIME_EXCEEDED);
+        assert_eq!(icmp_bytes[1], 0); // code
+        assert_eq!(&icmp_bytes[4..], &[0; 4]); // TTL exceeded doesn't use this field
+                                               // now unpack the bounced packet in the payload
+
+        let embedded_pkt = LaxPacketHeaders::from_ip(ttl_exceeded.payload.slice()).unwrap();
+        let ip_header = match embedded_pkt.net.unwrap() {
+            NetHeaders::Ipv4(ip4, _) => ip4,
+            _ => panic!("Didn't parse inner v4 IP header!?"),
+        };
+        use std::net::Ipv4Addr;
+        assert_eq!(
+            Ipv4Addr::from(ip_header.source),
+            "192.168.1.110".parse::<Ipv4Addr>().unwrap()
+        );
+        assert_eq!(
+            Ipv4Addr::from(ip_header.destination),
+            "216.239.38.120".parse::<Ipv4Addr>().unwrap()
+        );
+        let udp_header = embedded_pkt.transport.unwrap().udp().unwrap();
+        assert_eq!(udp_header.source_port, 49806); // numbers read from wireshark
+        assert_eq!(udp_header.destination_port, 33439);
+    }
+
+    const ICMP4_PORT_UNREACHABLE_BYTES: [u8; 70] = [
+        0x98, 0x8d, 0x46, 0xc5, 0x03, 0x82, 0x60, 0xa4, 0xb7, 0x25, 0x4b, 0x84, 0x08, 0x00, 0x45,
+        0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x39, 0x01, 0xc0, 0x47, 0xd8, 0xef, 0x26, 0x78,
+        0xc0, 0xa8, 0x01, 0x6e, 0x03, 0x03, 0xb3, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x45, 0x80, 0x00,
+        0x3c, 0xe3, 0xd2, 0x00, 0x00, 0x01, 0x11, 0x13, 0xe1, 0xc0, 0xa8, 0x01, 0x6e, 0xd8, 0xef,
+        0x26, 0x78, 0xb3, 0x4e, 0x82, 0xb2, 0x00, 0x28, 0x13, 0x1a,
+    ];
+    #[test]
+    fn icmp4_dst_unreachable() {
+        let offset = 14 + 20 + 1; // ethernet + iphdr + icmp_type
+                                  // test all of the unreachable codes to make sure the maps are right
+        for code_u8 in 0..icmpv4::CODE_DST_UNREACH_PRECEDENCE_CUTOFF {
+            let mut pkt = ICMP4_PORT_UNREACHABLE_BYTES.clone();
+            pkt[offset] = code_u8; // over write the code
+            let parsed = PacketHeaders::from_ethernet_slice(&pkt).unwrap();
+            let icmp4 = parsed.transport.unwrap().icmpv4().unwrap();
+            if let Icmpv4Type::DestinationUnreachable(icmp_code) = icmp4.icmp_type {
+                assert_eq!(code_u8, icmp_code.code_u8());
+            } else {
+                panic!("Not destination unreachable!?");
+            }
+        }
+    }
+}
diff --git a/tests/transport/icmpv6.rs b/tests/transport/icmpv6.rs
new file mode 100644
index 0000000..76d4502
--- /dev/null
+++ b/tests/transport/icmpv6.rs
@@ -0,0 +1,111 @@
+use super::super::*;
+
+mod regression {
+    use super::*;
+
+    #[test]
+    fn icmp6_echo_marshall_unmarshall() {
+        let icmp6 = Icmpv6Header {
+            icmp_type: Icmpv6Type::EchoRequest(IcmpEchoHeader { seq: 1, id: 2 }),
+            checksum: 0,
+        };
+        // serialize
+        let mut buffer: Vec<u8> = Vec::with_capacity(256);
+        icmp6.write(&mut buffer).unwrap();
+        let (new_icmp6, rest) = Icmpv6Header::from_slice(&buffer).unwrap();
+        assert_eq!(icmp6, new_icmp6);
+        assert_eq!(rest.len(), 0);
+    }
+
+    #[test]
+    fn ip6_echo_marshall_unmarshall() {
+        let builder = PacketBuilder::ipv6(
+            //source ip
+            [0xfe, 0x80, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
+            //dst ip
+            [0xfe, 0x80, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 114],
+            //time to life
+            20,
+        )
+        .icmpv6_echo_request(1, 2);
+        let payload = [0xde, 0xad, 0xbe, 0xef];
+        //get some memory to store the result
+        let mut result = Vec::<u8>::with_capacity(builder.size(payload.len()));
+
+        //serialize
+        builder.write(&mut result, &payload).unwrap();
+
+        let new_ip = PacketHeaders::from_ip_slice(&result).unwrap();
+        if let Some(TransportHeader::Icmpv6(hdr)) = new_ip.transport {
+            if let Icmpv6Type::EchoRequest(echo) = hdr.icmp_type {
+                assert_eq!(echo.id, 1);
+                assert_eq!(echo.seq, 2);
+            } else {
+                panic!("Not an EchoRequest!?");
+            }
+        } else {
+            panic!("No transport header found!?")
+        }
+    }
+    const ICMP6_ECHO_REQUEST_BYTES: [u8; 118] = [
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xdd, 0x60,
+        0x00, 0xf3, 0xc2, 0x00, 0x40, 0x3a, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0xd5, 0x2f, 0x00, 0x05,
+        0x00, 0x01, 0xe3, 0x58, 0xdb, 0x61, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xc0, 0x0d, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+        0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
+        0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+    ];
+    const ICMP6_ECHO_REPLY_BYTES: [u8; 118] = [
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xdd, 0x60,
+        0x00, 0xa3, 0xde, 0x00, 0x40, 0x3a, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x81, 0x00, 0xd4, 0x2f, 0x00, 0x05,
+        0x00, 0x01, 0xe3, 0x58, 0xdb, 0x61, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xc0, 0x0d, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+        0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
+        0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+    ];
+
+    #[test]
+    fn verify_icmp6_checksum() {
+        for (pkt, checksum) in [
+            (ICMP6_ECHO_REQUEST_BYTES, 0xd52f),
+            (ICMP6_ECHO_REPLY_BYTES, 0xd42f),
+        ] {
+            // make sure we can unmarshall the correct checksum
+            let request = PacketHeaders::from_ethernet_slice(&pkt).unwrap();
+            let mut icmp6 = request.transport.unwrap().icmpv6().unwrap();
+            let valid_checksum = icmp6.checksum;
+            assert_ne!(valid_checksum, 0);
+            assert_eq!(valid_checksum, checksum);
+            // reset it and recalculate
+            icmp6.checksum = 0;
+            let iph = match request.net {
+                Some(NetHeaders::Ipv6(ipv6, _)) => ipv6,
+                _ => panic!("Failed to parse ipv6 part of packet?!"),
+            };
+            assert_eq!(
+                icmp6
+                    .icmp_type
+                    .calc_checksum(iph.source, iph.destination, request.payload.slice()),
+                Ok(valid_checksum)
+            );
+        }
+    }
+
+    #[test]
+    fn echo_request_slice() {
+        let echo = SlicedPacket::from_ethernet(&ICMP6_ECHO_REQUEST_BYTES).unwrap();
+        use TransportSlice::*;
+        let icmp6 = match echo.transport.unwrap() {
+            Icmpv6(icmp6) => icmp6,
+            Icmpv4(_) | Udp(_) | Tcp(_) => panic!("Misparsed header!"),
+        };
+        assert!(matches!(
+            icmp6.header().icmp_type,
+            Icmpv6Type::EchoRequest(_)
+        ));
+    }
+}
diff --git a/tests/transport/mod.rs b/tests/transport/mod.rs
new file mode 100644
index 0000000..5a335d5
--- /dev/null
+++ b/tests/transport/mod.rs
@@ -0,0 +1,2 @@
+pub mod icmpv4;
+pub mod icmpv6;
diff --git a/tests/unit-tests.rs b/tests/unit-tests.rs
new file mode 100644
index 0000000..39cf6f7
--- /dev/null
+++ b/tests/unit-tests.rs
@@ -0,0 +1,2 @@
+use etherparse::*;
+mod transport;