IPv4/IPv6 Packet Fragmentation: Implementation Details

Introduction

In release v2.0, we’ve shipped PacketSmith with support for IPv4/IPv6 fragmentation detection and reassembly. Additionally, we’ve detailed some of the implementation details in the public article “IPv4/IPv6 Packet Fragmentation: Detection & Reassembly“. In version 3.0, we’ve shipped a full IPv4 and IPv6 packet fragmenter that can be invoked via the option frag_pkt (ipv4|ipv6):<mtu>. Crucially, the fragmenter engine understands IPv6 extensions, with their different types, to generate standard-compliant IPv6 fragmented packets.

IPv4 fragmentation logic is simpler to implement than IPv6’s. This is because IPv6 fragmentation requires careful handling of IPsec extensions and strict attention to the placement (or injection order) of the fragmentation header relative to other potential packet extensions.

You can fragment a packet capture’s packets at the IP level using a specific MTU (Maximum Transmission Unit) value. For IPv4 and IPv6, fragmentation is based on the header’s total length, such that if it exceeds the MTU, the original packet is fragmented (split into multiple IPv4/IPv6 packets) and replaced with the fragment packets.

Note-1: The checksum for each IP fragment can only be computed if the IP reassembly function is disabled. The default network behaviour is to reassemble fragments, so you must explicitly disable this using the -u, —upd_ip_lyr ip_frag_disable flag.

Note-2: The following definitions are used throughout the article:

  • The original packet is the packet to be fragmented.
  • Fragment packets are the fragmented packets of the original packet.

Note-3: To prevent console clutter from potentially hundreds of messages, error reporting for packet fragmentation failures is kept minimal. This applies whether the packet didn’t meet the fragmentation criteria or failed for another reason.

IPv4 Fragmentation

For IPv4, the MTU is defined as the maximum size of a packet, including the IP header and the payload; fragmentation can occur at either the host or the router.

When fragmenting an IPv4 packet that meets the MTU threshold, none of the layers before the IP layer is “considered”.  

The maximum fragment payload is derived as follows:

max_frag_payload_size = floor( (mtu – ip_header_len) / 8 ) * 8

  • The ip_header_len is the IPv4 header length, including any options.
  • The max_frag_payload_size is the IPv4 header length plus the IPv4 payload size.

The number of fragments is determined with the following equation:

num_of_frags = ceil(total_ip_payload_len / max_frag_payload_size)

  • The total_ip_payload_len is the IPv4 total_length field, which holds the length of the IPv4 header (including options) and the IPv4 payload size.

For calculating the last fragment payload length, the following equation is applied:

last_frag_payload_len = total_ip_payload_len – (num_of_frags – 1) * max_frag_payload_size

Some of the conditions applied when processing a packet for fragmentation include:

  • If the IPv4 Don’t Fragment flag is set, then the packet is skipped
  • If the IPv4 header is a 4-in-6 encapsulated header, then the packet is skipped
  • IPv4 header length cannot be greater than the provided MTU value
  • IPv4 header: More Fragments and Fragment Offset flags are set appropriately for each of the fragment packets
  • The IPv4 identification value for all fragment packets is inherited from the original packet
  • Payload size must be a multiple of 8 except for the last fragment packet
  • The packet’s original and captured length are updated accordingly to reflect the actual size of the packet
  • All the fragment packets inherit the same layers up to the IPv4 network layer of the original packet
  • When an IP packet is protected by IPsec, using Authentication Header (AH) and/or Encapsulation Security Payload (ESP), the AH and ESP headers are treated as part of the upper-layer payload. Therefore, fragmentation is performed on the entire IPsec-protected packet.

Once a packet is fragmented successfully, the original packet is replaced with the fragment packets. Internally, PacketSmith fragments all packets by storing their respective fragment frames in a separate buffer using a std::map data structure, with the key being the original packet frame number/id. Once fragmentation is complete, we derive the relative index to the original packet with respect to the number of fragment packets replacing it, inject the fragments, and then delete the original packet. No temporary pcap is created. This all happens in-memory and in-place.

IPv4 Fragmentation Example

Consider the following PCAP example ipv4_1506_bytes_frame.zip: It contains one packet with an IPv4 header that includes the following information:

Internet Protocol Version 4, Src: 192.168.2.14, Dst: 66.235.200.147
0100 .... = Version: 4
.... 0101 = Header Length: 20 bytes (5)
Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
0000 00.. = Differentiated Services Codepoint: Default (0)
.... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)
Total Length: 1492
Identification: 0xff29 (65321)
000. .... = Flags: 0x0
0... .... = Reserved bit: Not set
.0.. .... = Don't fragment: Not set
..0. .... = More fragments: Not set
...0 0000 0000 0000 = Fragment Offset: 0
Time to Live: 128
Protocol: TCP (6)
Header Checksum: 0x67c5 [correct]
Source Address: 192.168.2.14
Destination Address: 66.235.200.147

Let’s fragment this packet with an MTU of 512, using the following PacketSmith option:

PacketSmith.exe -i ipv4_1506_bytes_frame.pcap -o ipv4_frag.pcap —frag_pkt ipv4:512

The expectation from executing the above command with respect to the equations defined earlier is as follows:

  • max_frag_payload_size = floor( (512 – 20) / 8) * 8 = 488
  • num_of_frags = ceil(1492 / 488) = 4
  • last_frag_payload_len = 1492 – (4 – 1) * 488 = 1492 – 1464 = 28

The resulting PCAP has been attached as ipv4_frag.zip. All of the fragment packets got incorrect checksums; to fix them, we execute the following command, with the —upd_ip_lyr ip_frag_disable flag as mentioned in the introduction in Note-1.

PacketSmith.exe -i ipv4_frag.pcap -o ipv4_frag_chksum_fixed.pcap —checksum —upd_ip_lyr ip_frag_disable

The final pcap has been attached as ipv4_frag_chksum_fixed.zip, with the following screenshot:

ipv4_4_frag_pkts
Fig.1 IPv4 Fragment Packets

The IPv4 fields in Figure 1 match the equations’ numbers. Note that for the Fragment Offset (FO), you need to multiply it by 8.

IPv6 Fragmentation

Fragmenting an IPv6 packet is more subtle and requires careful consideration of various rules and conditions for a successful operation, since this entails the injection of a fragment extension in a specific order. The minimum MTU value for IPv6 fragmentation is 1280 bytes; however, PacketSmith doesn’t enforce this limit, instead setting the minimum to 48 (IPv6 header length/40 + fragment extension length/8). You should avoid setting this value too low. If the setting doesn’t satisfy the upper-layer protocol’s minimum length condition, the protocol dissector will encounter errors during parsing.

Some of the conditions applied when processing a packet for fragmentation include:

  • If the IPv6 fragment extension already exists, then the packet is skipped 

Each extension header should occur at most once, except for the Destination Options header, which should occur at most twice (once before a Routing header and once before the upper-layer header).

  • If the IPv6 header is a 6-in-4 encapsulated header, then the packet is skipped
  • (IPv6 payload length + header length) cannot be greater than the provided MTU value
  • The IPv6 fragment extension is constructed in-place and inserted into every fragment packet 
    • The identification field is pseudo-randomly generated for all fragment packets of the original packet
    • More Fragments and Offset flags are set appropriately for each of the fragment packets
  • Payload size must be a multiple of 8 except for the last fragment packet
  • The packet’s original and captured length are updated accordingly to reflect the actual size of the packet
  • All the fragment packets inherit the same layers up to the IPv6 network layer of the original packet
  • When an IP packet is protected by IPsec, using Authentication Header (AH) and/or Encapsulation Security Payload (ESP), the ESP header is treated as part of the upper-layer payload. All other extension headers are carried over with every fragment packet.

The fragment extension placement order follows the RFC 8200, section 4.1, “Extension Header Order”:

“When more than one extension header is used in the same packet, it is recommended that those headers appear in the following order:
 
IPv6 header
Hop-by-Hop Options header
Destination Options header (note 1)
Routing header
Fragment header
Authentication header (note 2)
Encapsulating Security Payload header (note 2)
Destination Options header (note 3)
Upper-Layer header”

The ESP header isn’t a standard IPv6 extension; it’s an upper-layer protocol. PacketSmith is designed to parse IPv6 packets with these headers correctly.

Once a packet is fragmented successfully, the original packet is replaced with the fragment packets; the same process as with IPv4 fragmentation.

The same equations listed above apply here with the IPv6 version. 

IPv6 Fragmentation Example

Consider the following PCAP example ipv6_122_bytes_frame_with_esp.zip: It contains one packet with an IPv6 header that includes the following information:

Internet Protocol Version 6, Src: fe80::222:22ff:fe22:2222, Dst: ff02::5
0110 .... = Version: 6
.... 1110 0000 .... .... .... .... .... = Traffic Class: 0xe0 (DSCP: CS7, ECN: Not-ECT)
.... 1110 00.. .... .... .... .... .... = Differentiated Services Codepoint: Class Selector 7 (56)
.... .... ..00 .... .... .... .... .... = Explicit Congestion Notification: Not ECN-Capable Transport (0)
.... 0000 0000 0000 0000 0000 = Flow Label: 0x00000
Payload Length: 68
Next Header: Encap Security Payload (50)
Hop Limit: 1
Source Address: fe80::222:22ff:fe22:2222
Destination Address: ff02::5
[Source SLAAC MAC: Schaffne_22:22:22 (00:22:22:22:22:22)]
[Stream index: 0]

As shown above, the Payload Length is set to 68, which is the length of the ESP upper-layer protocol “extension”. The IPv6 header is a fixed 40 bytes with no extensions. The Next Header field points to the upper-layer protocol “extension”, that’s ESP (50).

Let’s fragment this packet with an MTU of 64, using the following PacketSmith option:

PacketSmith.exe -i ipv6_122_bytes_frame_with_esp.pcap -o ipv6_frag.pcap —frag_pkt ipv6:64

The expectation from executing the above command with respect to the equations defined earlier is as follows:

  • max_frag_payload_size = floor( (64 – 40) / 8) * 8 = 24
    • Actually, the effective fragment payload size is 16 bytes and not 24, since for every fragment packet, an 8-byte fragment extension is added. We’re subtracting the fragment size from the max_frag_payload_size.
  • num_of_frags = ceil(108/ 24) = 5
    • The total_ip_payload_len in the IPv6 header represents the header length, plus the length of any extensions that might exist, and the length of the IPv6 payload. In this case, it is: (40 + 0 + 68) = 108 bytes.
  • last_frag_payload_len = 108 – (5 – 1) * 24 = 12

The resulting PCAP has been attached as ipv6_frag.zip, with the following screenshot:

Fig. 2 IPv6 Fragment Packets

The fragment extension has been added to every fragment packet, as shown in Figure 2, under the column “Fragment Header for IPv6”. The offsets in the IPv6 fragment extension in Figure 2 match the equations’ numbers. The original packet doesn’t carry any extension other than the ESP upper-layer protocol; therefore, the fragment extension is the only extension that exists in the fragment packets.

The following is a gallery of details of all 5 IPv6 fragment packets shown in Figure 2:

Notice how the fragment extension is injected into every fragment packet, including the last one, with all the required fields set as per the equations’ calculations. Moreover, the IPv6 Next Header field has been updated to point to the injected fragment extension (Fragment Header for IPv6 (44)), and the fragment extension Next header field has been updated to point to the following upper-layer protocol, that is, ESP.

Conclusion

In this article, we have shown some of the implementation details behind the IPv4 and IPv6 fragmenter engine in PacketSmith, with demonstrable examples. Moreover, we have called out the interplay between the auto IP reassembly engine and the checksum calculations, which requires disabling the IP reassembly engine to perform checksum per fragment packet.


Author: Mohamad Mokbel (Netomize)

First release: October 01, 2025