Fragmentation
Reassembly
Read aloud is unavailable in this browser.
Putting the Pieces Back Together
Fragmentation is only half the story. For IP to work, the destination host must be able to take fragments that may arrive out of order, be delayed, or even be duplicated—and reconstruct the original datagram perfectly. This process of reassembly is deceptively complex, involving careful state management, timeout handling, and validation against malicious or malformed fragments.
Reassembly is exclusively a destination-host responsibility. Intermediate routers never attempt to reassemble fragments—they simply forward (or further fragment if necessary). This end-to-end design principle keeps routers fast and simple while placing complexity at endpoints that can better manage the state.
By the end of this page, you will understand reassembly from the inside out: how destination hosts identify and group fragments, the data structures used for reassembly, handling of out-of-order arrival, timeout mechanisms, error conditions, performance considerations, and the security challenges that have made reassembly a frequent target of attacks.
The Reassembly Key
The first challenge in reassembly is grouping fragments that belong together. Multiple fragmented datagrams may be in transit simultaneously from different sources or even from the same source to different destinations. The destination must correctly associate each incoming fragment with its original datagram.
- •Source IP Address — The originator of the datagram
- •Destination IP Address — The recipient (this host)
- •Protocol Field — TCP, UDP, ICMP, etc. (value from IP header)
- •Identification Field — 16-bit unique ID set by source
Why all four fields are necessary:
Consider each field's contribution:
- Without Source IP: Fragments from different sources with same ID would be mixed
- Without Destination IP: On multi-homed hosts, fragments to different interfaces could conflict
- Without Protocol: Different protocols from same host could have same ID values
- Without Identification: All traffic from same source would be conflated
Reassembly buffer lookup:
When a fragment arrives:
key = hash(src_ip, dst_ip, protocol, identification)
buffer = reassembly_table.lookup(key)
if buffer exists:
add_fragment_to_buffer(buffer, fragment)
else:
create_new_buffer(key)
add_fragment_to_buffer(new_buffer, fragment)
With only 65,536 possible Identification values, high-traffic hosts can exhaust IDs quickly. On a 1 Gbps link sending 1000-byte datagrams, IDs recycle every 524 milliseconds. If old fragments are still in transit when IDs recycle, incorrect reassembly can occur. RFC 6864 addresses this by clarifying when unique IDs are required and recommending techniques like per-destination ID counters.
Reassembly Buffer Management
Each datagram undergoing reassembly requires dedicated buffer space. The buffer must track which bytes have been received, handle out-of-order arrival, and detect when reassembly is complete.
- •Reassembly key — The four-tuple identifying this datagram
- •Data buffer — Space for the original datagram's data (up to 65,515 bytes)
- •Bit vector or hole list — Tracks which byte positions have been received
- •Total data length — Set when last fragment (MF=0) arrives; offset×8 + data_length
- •First fragment header — IP header from first fragment (contains options, TTL, etc.)
- •Timer — Countdown until buffer expiration
- •Fragment count — Optional; for statistics/debugging
Hole tracking algorithms:
Two common approaches track which bytes are missing:
1. Bit Vector Approach:
- One bit per 8-byte block (matching fragment offset granularity)
- For 65,535-byte datagram: 8,192 bits = 1,024 bytes of bitmap
- Simple to implement; fixed memory overhead
- Complete when all bits are set
2. Hole Descriptor List:
- Linked list of (start, end) pairs describing gaps
- Initially: [(0, ∞)] — entire datagram is a hole
- As fragments arrive, holes are split or removed
- Complete when hole list is empty
- Memory-efficient for datagrams with few fragments
Completion detection:
Reassembly is complete when:
- Total size is known (last fragment received, MF=0)
- All bytes from 0 to (total_size - 1) are present
- No holes remain in the coverage
A host under attack could receive millions of fragment streams, each requiring buffer allocation. Operating systems limit reassembly memory through maximum buffer counts, per-source limits, and aggressive timeout policies. Without these limits, fragment attacks become trivial denial-of-service vectors.
Step-by-Step Reassembly Process
Let's trace through the complete reassembly of a fragmented datagram to understand each decision point.
Detailed algorithm:
123456789101112131415161718192021222324252627282930313233343536373839404142434445
def process_fragment(fragment): # Step 1: Extract reassembly key key = (fragment.src_ip, fragment.dst_ip, fragment.protocol, fragment.identification) # Step 2: Find or create reassembly buffer buffer = reassembly_table.get(key) if not buffer: buffer = create_reassembly_buffer(key) buffer.timer = start_timer(REASSEMBLY_TIMEOUT) # Typically 30-60 seconds reassembly_table[key] = buffer # Step 3: Calculate byte range for this fragment start_byte = fragment.offset * 8 end_byte = start_byte + len(fragment.data) # Step 4: Validate fragment if end_byte > MAX_IP_DATAGRAM_SIZE: drop_and_log("Fragment exceeds maximum datagram size") return # Step 5: Check for overlapping data (security!) if buffer.has_data_in_range(start_byte, end_byte): # Policy decision: drop entire datagram or use first data handle_overlap(buffer, fragment) return # Step 6: Store fragment data buffer.store_data(fragment.data, start_byte) buffer.update_hole_list(start_byte, end_byte) # Step 7: If first fragment, save header if fragment.offset == 0: buffer.first_fragment_header = fragment.ip_header # Step 8: If last fragment, record total size if fragment.more_fragments == 0: buffer.total_size = end_byte # Step 9: Check if reassembly is complete if buffer.total_size and buffer.is_complete(): # All bytes received! complete_datagram = reassemble(buffer) deliver_to_protocol(complete_datagram) cleanup_buffer(key)The last fragment (MF=0) might arrive first, providing total_size before any data. Middle fragments might arrive before the first fragment. A robust implementation handles all arrival orders correctly—the only requirement is that ALL fragments eventually arrive before timeout.
Timeout and Error Handling
Reassembly cannot wait indefinitely. Networks lose packets, and incomplete fragment streams must be cleaned up to free resources. Timeout and error handling are critical for robust operation.
- •Timer starts on first fragment arrival — Not on last or any specific fragment
- •RFC 791 recommends 15 seconds minimum — Modern implementations typically use 30-60 seconds
- •On timeout: discard all received fragments — Partial data is useless and cannot be delivered
- •Send ICMP Time Exceeded (Type 11, Code 1) — 'Fragment reassembly time exceeded' if first fragment was received
- •No ICMP if first fragment missing — Cannot determine source without first fragment's header
- •Free buffer resources — Reclaim memory for other reassembly operations
ICMP Time Exceeded message:
When reassembly times out and the first fragment was received, the host sends:
- ICMP Type 11: Time Exceeded
- Code 1: Fragment reassembly time exceeded
- Includes: First fragment's IP header + 8 bytes of data
This informs the source that its datagram could not be delivered, allowing upper layers to take action (e.g., TCP retransmission at smaller size).
| Error Condition | Detection | Action |
|---|---|---|
| Timeout | Timer expires before completion | Discard fragments, send ICMP if possible, free buffer |
| Overlapping fragments | New fragment data range overlaps existing | Policy-dependent: drop new, drop all, or first-wins |
| Total size exceeded | Last fragment indicates > 65,535 bytes | Discard all fragments immediately |
| Inconsistent total size | Different 'last' fragments disagree on size | Discard all fragments as corrupted |
| Memory exhaustion | No buffer space for new fragmentstream | Drop incoming fragment, possibly oldest buffers |
| Invalid offset alignment | Non-last fragment data not 8-byte aligned | Implementation-dependent; may accept or reject |
Short timeouts conserve memory but may drop legitimate traffic on high-latency paths. Long timeouts are vulnerable to resource exhaustion attacks. Most systems allow tuning: Linux's net.ipv4.ipfrag_time defaults to 30 seconds. In high-security environments, consider 15-20 seconds.
Overlap Handling Policies
When fragments claim to contain overlapping byte ranges, the implementation must decide how to handle the conflicting data. This is a significant security consideration, as overlap attacks have been used to evade detection.
- •Keep data from first-arriving fragment
- •Ignore overlapping data in later fragments
- •Simpler to implement
- •Historically common (Windows)
- •Vulnerable to certain attacks
- •Overwrite with later-arriving fragment
- •Newer data replaces older
- •Supports legitimate retransmissions
- •Some BSD systems used this
- •Also vulnerable to attacks
The overlap attack problem:
Attackers exploit different overlap policies:
- First fragment: Contains benign data matching firewall rules
- Second fragment (overlapping): Contains malicious payload at same offset
If the firewall uses first-wins policy but the target uses last-wins, the firewall sees benign data while the target receives the malicious payload.
Modern solution: Drop on overlap
RFC 1858 and RFC 3128 recommend dropping the entire datagram if ANY overlap is detected:
- Overlapping fragments indicate either an attack or severe network malfunction
- Legitimate traffic should never produce overlaps
- Dropping is the safest policy
Most modern operating systems now implement drop-on-overlap as the default policy.
123456789101112131415161718192021222324252627282930
def check_overlap(buffer, new_start, new_end): """ Check if new fragment overlaps with existing data. Modern security practice: return True if ANY overlap exists. """ for (existing_start, existing_end) in buffer.received_ranges: # Check all overlap conditions if new_start < existing_end and new_end > existing_start: # Overlap detected! log_security_event("Fragment overlap detected", buffer.key, new_start, new_end, existing_start, existing_end) return True return False def handle_overlap_secure(buffer, fragment): """ Secure handling: drop entire datagram on overlap. """ log_alert("Dropping datagram due to fragment overlap", buffer.key) # Generate ICMP error if we have first fragment if buffer.first_fragment_header: send_icmp_param_problem(buffer.first_fragment_header) # Destroy buffer and free resources destroy_reassembly_buffer(buffer) # Update statistics stats.overlapping_fragments_dropped += 1Overlapping fragments are a red flag for attack activity. In security-conscious environments, not only should overlapping datagrams be dropped, but the source IP should be logged and potentially blocked. Legitimate traffic simply does not produce overlapping fragments.
Performance Considerations
Reassembly consumes system resources: memory for buffers, CPU for processing, and data structure overhead. Understanding performance implications helps in system design and capacity planning.
- •Buffer memory: Up to 65,535 bytes per incomplete datagram, plus metadata overhead
- •Hash table operations: Lookup on every fragment arrival; scales with active reassembly count
- •Hole list maintenance: Splitting/merging hole entries on each fragment
- •Timer management: Each buffer has associated timeout timer requiring kernel resources
- •Lock contention: Multi-core systems may contend on reassembly table locks
- •Copy operations: Fragment data typically copied into reassembly buffer
| Parameter | Default | Description |
|---|---|---|
| ipfrag_high_thresh | 4 MB | Max memory for all reassembly buffers; new fragments dropped when exceeded |
| ipfrag_low_thresh | 3 MB | When high threshold hit, purge down to this level |
| ipfrag_time | 30 seconds | Reassembly timeout period |
| ipfrag_max_dist | 64 | Maximum packet reordering distance (optimization) |
| ipfrag_secret_interval | 600 seconds | How often to regenerate hash secret (security) |
123456789101112131415
# View current fragment settingssysctl -a | grep ipfrag # Common output:net.ipv4.ipfrag_high_thresh = 4194304net.ipv4.ipfrag_low_thresh = 3145728net.ipv4.ipfrag_time = 30net.ipv4.ipfrag_max_dist = 64 # Monitor fragment statisticscat /proc/net/snmp | grep Ip# Relevant fields: ReasmTimeout, ReasmReqds, ReasmOKs, ReasmFails # Detailed fragment statscat /proc/net/ip_conntrack_stats # If conntrack enabledThe best performing reassembly is no reassembly at all. Design networks and applications to avoid fragmentation: use Path MTU Discovery, configure appropriate MSS, and ensure MTU consistency across your network. Reassembly should be an exception, not the norm.
Security Attacks Targeting Reassembly
Reassembly has been a rich target for attacks over the decades. Understanding these attack patterns is essential for security engineering and helps appreciate why modern implementations include various protections.
- •Teardrop Attack (1997) — Overlapping fragments with invalid offsets caused Windows/Linux to crash during reassembly. The overlap calculations produced negative numbers, corrupting memory.
- •Ping of Death (1996) — Fragment offsets calculated to produce reassembled datagram > 65,535 bytes. Buffer overflow when kernel tried to allocate impossibly large buffer.
- •Rose Attack — Send first fragment, never send the rest. Repeat thousands of times. Victim's reassembly buffers fill up, denying service to legitimate traffic.
- •Jolt/Jolt2 Attack — Stream of identical fragments with same offset, overwhelming reassembly logic with duplicate handling.
- •NewTear Attack — Variant of Teardrop with carefully crafted offsets to bypass initial patches.
- •Bonk/Boink Attacks — Similar to Teardrop, exploiting specific Windows CE and older Windows reassembly bugs.
- •Fragment Cache Poisoning — Inject fragments that will be combined with legitimate traffic, modifying seemingly-authentic packets.
Modern Defenses:
Operating systems and security devices now implement multiple layers of protection:
- Strict validation: Reject fragments that would produce oversized datagrams
- Drop overlapping fragments: Treat overlap as attack indicator
- Rate limiting: Limit fragment processing rate per-source and globally
- Memory limits: Hard caps on reassembly buffer memory
- Short timeouts: Reduce window for attack completion
- Random IP IDs: Prevent attackers from predicting identification values
- Network filtering: Drop fragments at network edge before reaching hosts
Don't rely solely on host reassembly defenses. Implement fragment protection at multiple layers: edge firewalls should drop/rate-limit fragments, IPS systems should detect fragment attacks, and hosts should have hardened reassembly implementations. Layered defense provides resilience against novel attack variants.
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.