Fragmentation
Fragment Offset
Read aloud is unavailable in this browser.
The Puzzle Piece Numbering System
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.
Fragment Offset in the IPv4 Header
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.
- •Field size: 13 bits, allowing values from 0 to 8191
- •Units: 8-byte blocks (not bytes). Actual byte offset = Fragment Offset × 8
- •Maximum byte offset: 8191 × 8 = 65,528 bytes
- •First fragment: Always has Fragment Offset = 0
- •Subsequent fragments: Offset indicates position in original datagram's data portion
- •Does NOT include headers: Offset refers to data position, not including IP headers
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.
Understanding Offset Calculation
Let's trace through a complete fragmentation example to understand how Fragment Offset values are determined.
Scenario:
- Original IP datagram: 4000 bytes total (20-byte header + 3980 bytes data)
- MTU limit: 1500 bytes
- Maximum data per fragment: 1500 - 20 = 1480 bytes
- 1480 must be divisible by 8 ✓ (1480 ÷ 8 = 185)
| 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:
- Carries bytes 0-1479 of original data (1480 bytes)
- Fragment Offset = 0 / 8 = 0
- More Fragments (MF) = 1 (there are more fragments)
- Total fragment size: 1480 + 20 = 1500 bytes
Fragment 2:
- Carries bytes 1480-2959 of original data (1480 bytes)
- Fragment Offset = 1480 / 8 = 185
- More Fragments (MF) = 1 (still more to come)
- Total fragment size: 1480 + 20 = 1500 bytes
Fragment 3:
- Carries bytes 2960-3979 of original data (1020 bytes)
- Fragment Offset = 2960 / 8 = 370
- More Fragments (MF) = 0 (this is the last fragment)
- Total fragment size: 1020 + 20 = 1040 bytes
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 8-Byte Alignment Constraint
The requirement that Fragment Offset is measured in 8-byte units has critical implications for fragmentation behavior.
- •All fragments except the last must have data length divisible by 8. If MTU - header_size isn't divisible by 8, round down to the nearest multiple of 8.
- •The last fragment has no alignment requirement. It carries whatever data remains, which may be any number of bytes.
- •IP options affect alignment calculations. Options increase header size, reducing available data space. The remaining space must still be 8-byte aligned.
- •Fragments can be further fragmented. If a fragment encounters a smaller MTU, it can be split again—each new fragment's data (except the last) must be 8-byte aligned.
Example: MTU that doesn't align naturally
Suppose MTU = 1006 bytes (an odd historical value):
- Available for data: 1006 - 20 = 986 bytes
- 986 ÷ 8 = 123.25 (not a whole number)
- Must round down: 123 × 8 = 984 bytes per fragment
- Each fragment (except last): 984 bytes of data + 20 bytes header = 1004 bytes
- 2 bytes of MTU 'wasted' per fragment due to alignment
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.
Relationship with Identification Field
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.
- •16-bit field — Values 0-65535, wraps around after exhaustion
- •Set by source host — Unique (ideally) for each datagram during its expected lifetime on the network
- •Copied to all fragments — When a datagram is fragmented, all fragments receive the same Identification value
- •Combined with source IP + destination IP + protocol — These four fields together uniquely identify an original datagram for reassembly
Reassembly key = (Source IP, Destination IP, Protocol, Identification)
The receiving host uses this 4-tuple to group fragments:
- Fragment arrives with given 4-tuple
- Host checks for existing reassembly buffer with same 4-tuple
- If found: add fragment to buffer based on Fragment Offset
- If not found: create new reassembly buffer
- When all fragments received (no gaps, last fragment identified): reassemble and deliver
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.
Fragment Offset in Packet Analysis
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:
- Identification: Unique ID linking all fragments (6699 in example)
- Fragment offset: Position in 8-byte units (Wireshark shows raw value; multiply by 8 for bytes)
- Flags [+]: The '+' in tcpdump indicates More Fragments flag is set
- frag notation: Shows ID, fragment length, offset in bytes, and '+' if more fragments
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 position- The complete datagram appears on the last fragment
ip.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.
Nested Fragmentation Scenarios
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.
Edge Cases and Malformed Fragments
Understanding edge cases in Fragment Offset handling is important for both network security and implementation correctness.
- •Overlapping fragments — Two fragments claim to contain the same byte positions. Historically used in attacks (Teardrop). Modern stacks either drop overlapping fragments or use first-wins policy.
- •Gaps in fragments — Missing fragment offsets leave holes. Cannot reassemble until all fragments arrive or timeout expires.
- •Fragment extends beyond 65,535 bytes — Last fragment's offset + length exceeds maximum IP datagram size. Must be rejected as malformed.
- •Zero-length last fragment — Fragment with MF=0 and zero data. Legal but unusual; may indicate implementation bug.
- •First fragment without transport header — Tiny first fragment (TCP header in second fragment). Historically used to bypass firewalls. Modern firewalls require minimum first-fragment size.
- •Maximum offset with MF=1 — Offset 8191 with More Fragments set implies datagram > 65,535 bytes. Invalid.
Reassembly validation checks:
Compliant IP stacks must validate:
-
No fragment exceeds maximum datagram size:
- offset × 8 + fragment_data_length ≤ 65,515 (65,535 - 20)
-
Last fragment consistency:
- If MF=0, this determines total datagram size
- All fragments must fit within this total
-
No overlaps:
- Fragment byte ranges must not conflict
- Modern policy: drop entire datagram on overlap
-
Complete coverage:
- No gaps between offset 0 and end of last fragment
- All positions must have data before reassembly
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.
KEEP LEARNING
There’s more to understand.
You’re reading the introduction. Create a free account to read the complete lesson, save notes and keep track of your progress.