Loading learning content...
When a jigsaw puzzle is manufactured, each piece is numbered on the back so workers can verify completeness and locate any missing pieces. IP fragmentation uses an analogous system: the Fragment Offset field tells the destination exactly where each fragment's data belongs in the original datagram.
Without Fragment Offset, reassembly would be impossible—fragments could arrive out of order, be delayed by different routing paths, or even be duplicated. The offset provides the essential positioning information that transforms a jumble of fragments back into the original packet.
By the end of this page, you will master the Fragment Offset field completely: how it's calculated, why it uses 8-byte units, how it enables reassembly, and how to trace through fragmentation scenarios step-by-step. You'll be able to analyze packet captures and calculate fragment boundaries with confidence.
The Fragment Offset field occupies bits 19-31 (counting from 0) of the IPv4 header—a 13-bit field within the 32-bit word that also contains the Flags field.
Using 8-byte units instead of bytes serves two purposes: (1) It extends the range—13 bits can address 65,528 bytes instead of just 8,191 bytes, covering the maximum 65,535-byte IP datagram. (2) It forces fragment boundaries to 8-byte alignments, simplifying reassembly buffer management and ensuring fragments can be placed in memory without complex partial-byte handling.
Let's trace through a complete fragmentation example to understand how Fragment Offset values are determined.
Scenario:
| Fragment | Data Bytes | Byte Range | Offset Value | Offset (bytes) | MF Flag |
|---|---|---|---|---|---|
| 1 | 1480 | 0 - 1479 | 0 | 0 | 1 (more) |
| 2 | 1480 | 1480 - 2959 | 185 | 1480 | 1 (more) |
| 3 | 1020 | 2960 - 3979 | 370 | 2960 | 0 (last) |
Step-by-step calculation:
Fragment 1:
Fragment 2:
Fragment 3:
Fragment Offset = (Starting byte position in original data) ÷ 8
For any fragment N (0-indexed): Offset = (N × MaxPayload) ÷ 8
Where MaxPayload = MTU - IP_Header_Size, rounded down to multiple of 8.
The requirement that Fragment Offset is measured in 8-byte units has critical implications for fragmentation behavior.
Example: MTU that doesn't align naturally
Suppose MTU = 1006 bytes (an odd historical value):
Forgetting to align fragment data sizes to 8-byte boundaries is a common implementation error. This causes reassembly bugs: fragments appear to overlap (if too much data included) or leave gaps (if too little). The receiving host must reject such malformed fragments.
Fragment Offset alone isn't sufficient for reassembly—fragments from different original datagrams could have the same offsets. The Identification field links fragments belonging to the same original datagram.
Reassembly key = (Source IP, Destination IP, Protocol, Identification)
The receiving host uses this 4-tuple to group fragments:
Timeout consideration:
If not all fragments arrive within a timeout period (typically 15-60 seconds), the partial datagram is discarded. This prevents zombie reassembly buffers from consuming memory indefinitely.
| Source IP | Dest IP | Protocol | ID | Offset | MF | Interpretation |
|---|---|---|---|---|---|---|
| 192.168.1.1 | 10.0.0.1 | TCP (6) | 12345 | 0 | 1 | Datagram A, Fragment 1 |
| 192.168.1.2 | 10.0.0.1 | TCP (6) | 12345 | 0 | 1 | Datagram B, Fragment 1 (different source) |
| 192.168.1.1 | 10.0.0.1 | TCP (6) | 12345 | 185 | 0 | Datagram A, Fragment 2 |
| 192.168.1.2 | 10.0.0.1 | TCP (6) | 12345 | 185 | 0 | Datagram B, Fragment 2 |
With only 16 bits, Identification values recycle every 65,536 datagrams. On high-speed links, this can happen within seconds. If old fragments are still in transit when IDs recycle, misassembly can occur. Modern hosts use randomized Identification values rather than sequential counters to mitigate this, and RFC 6864 clarified when the field must be unique.
Understanding Fragment Offset is essential for analyzing network captures. Let's examine how this appears in real tools.
1234567891011121314151617181920212223242526
Internet Protocol Version 4, Src: 192.168.1.100, Dst: 10.0.0.50 0100 .... = Version: 4 .... 0101 = Header Length: 20 bytes (5) Total Length: 1500 Identification: 0x1a2b (6699) Flags: 0x2000, More fragments 0... .... .... .... = Reserved bit: Not set .0.. .... .... .... = Don't fragment: Not set ..1. .... .... .... = More fragments: Set Fragment offset: 0 Time to live: 64 Protocol: TCP (6) [Reassembled IPv4 in frame: 45] # Second fragment: Identification: 0x1a2b (6699) <- Same as first Flags: 0x2000, More fragments Fragment offset: 185 <- 185 × 8 = 1480 bytes [This is fragment 2 of 3] # Last fragment: Identification: 0x1a2b (6699) <- Same as first Flags: 0x0000 ..0. .... .... .... = More fragments: Not set Fragment offset: 370 <- 370 × 8 = 2960 bytes [This is the last fragment]Reading fragment information:
Wireshark reassembly:
Wireshark automatically reassembles fragments and shows:
[Reassembled IPv4 in frame: X] - tells you which captured frame contains the complete reassembled datagram[This is fragment N of M] - fragment positionip.frag_offset > 0 shows non-first fragments
ip.flags.mf == 1 shows fragments with More Fragments set
ip.frag_offset > 0 or ip.flags.mf == 1 shows all fragment-related traffic
These filters help quickly identify fragmentation in captures.
A fragment can itself be fragmented if it encounters a smaller MTU downstream. This creates complex scenarios where Fragment Offset values must be carefully computed.
Scenario: Cascade fragmentation
Original datagram: 4000 bytes (20 header + 3980 data) Router R1 (MTU 1500): Fragments into 3 pieces Router R2 (MTU 500): Must further fragment each piece
| Fragment | Data | Original Byte Range | Offset | MF |
|---|---|---|---|---|
| A | 1480 bytes | 0-1479 | 0 | 1 |
| B | 1480 bytes | 1480-2959 | 185 | 1 |
| C | 1020 bytes | 2960-3979 | 370 | 0 |
| Original Frag | Sub-Fragment | Data | Byte Range (in original) | New Offset | MF |
|---|---|---|---|---|---|
| A (offset 0) | A1 | 480 bytes | 0-479 | 0 | 1 |
| A (offset 0) | A2 | 480 bytes | 480-959 | 60 | 1 |
| A (offset 0) | A3 | 480 bytes | 960-1439 | 120 | 1 |
| A (offset 0) | A4 | 40 bytes | 1440-1479 | 180 | 1 |
| B (offset 185) | B1 | 480 bytes | 1480-1959 | 185 | 1 |
| B (offset 185) | B2 | 480 bytes | 1960-2439 | 245 | 1 |
| B (offset 185) | B3 | 480 bytes | 2440-2919 | 305 | 1 |
| B (offset 185) | B4 | 40 bytes | 2920-2959 | 365 | 1 |
| C (offset 370) | C1 | 480 bytes | 2960-3439 | 370 | 1 |
| C (offset 370) | C2 | 480 bytes | 3440-3919 | 430 | 1 |
| C (offset 370) | C3 | 60 bytes | 3920-3979 | 490 | 0 |
Key observations:
Offset addition: When fragment B (offset 185 = 1480 bytes) is further fragmented, its sub-fragments have offsets relative to the ORIGINAL datagram: 1480, 1960, 2440, 2920 bytes.
MF flag propagation: The last sub-fragment of fragment A has MF=1 because fragment A was not the last original fragment. Only C3 (last sub-fragment of last fragment) has MF=0.
Destination sees all 11 fragments: The receiving host doesn't know about the cascade; it just sees 11 fragments with offsets ranging from 0 to 490 (× 8 = 3920 bytes).
Same Identification: All 11 fragments share the same Identification value, enabling reassembly.
With 11 fragments instead of 3, the probability of complete delivery drops significantly. If 1% packet loss per hop, probability of losing at least one fragment over 5 hops increases dramatically. This is why Path MTU Discovery and DF bit are preferred over relying on fragmentation.
Understanding edge cases in Fragment Offset handling is important for both network security and implementation correctness.
Reassembly validation checks:
Compliant IP stacks must validate:
No fragment exceeds maximum datagram size:
Last fragment consistency:
No overlaps:
Complete coverage:
12345678910111213141516171819202122232425262728293031323334
def validate_fragment(frag, reassembly_buffer): """Validate incoming fragment for security and correctness""" # Calculate byte range start_byte = frag.offset * 8 end_byte = start_byte + frag.data_length # Check 1: Maximum datagram size if end_byte > 65515: drop_fragment("exceeds maximum datagram size") return False # Check 2: Overlap detection for existing in reassembly_buffer.fragments: if ranges_overlap(start_byte, end_byte, existing.start, existing.end): drop_entire_datagram("overlapping fragments") return False # Check 3: If last fragment, verify total size consistent if not frag.more_fragments: expected_total = end_byte if reassembly_buffer.expected_total and \ reassembly_buffer.expected_total != expected_total: drop_entire_datagram("inconsistent last fragment") return False reassembly_buffer.expected_total = expected_total # Check 4: First fragment minimum size (security) if frag.offset == 0 and frag.data_length < 68: # May not contain full TCP/UDP header log_warning("suspicious small first fragment") return TrueFragment handling has been a rich source of security vulnerabilities. Teardrop, Bonk, Boink, and numerous other attacks exploited faulty reassembly logic. Modern networking stacks apply strict validation, but unusual fragment patterns should always raise suspicion in security monitoring.
You now have comprehensive knowledge of the Fragment Offset field—the essential mechanism that enables IP datagram reassembly. Let's consolidate with key formulas and takeaways.
byte_position = fragment_offset × 8max_data = floor((MTU - IP_header_size) / 8) × 8offset = (N × max_data) / 8ceil(total_data / max_data)N × (max_data + header) + (last_frag_data + header)(N - 1) × IP_header_sizeWhat's Next:
We've examined what the Fragment Offset field does. Next, we'll explore the MF (More Fragments) and DF (Don't Fragment) flags—the control bits that determine whether fragmentation should occur and how to identify the final fragment in a sequence.
You now understand Fragment Offset deeply—from the header field structure through calculation procedures, alignment constraints, nested fragmentation, and edge cases. This knowledge is essential for packet analysis, network troubleshooting, and understanding fragmentation-related security issues.