IP Fragmentation and the TTL Field

Every network link has a maximum transmission unit — the largest frame it can carry. These limits differ wildly across a path. Ethernet typically caps payloads near 1500 bytes. Some tunnels shrink that further, and a few links allow jumbo frames well beyond it. When a router receives an IP datagram destined for a link whose MTU is smaller than the datagram, something has to give. IP's answer, defined alongside The IP Protocol and Datagram Format, is fragmentation. Split the datagram into smaller pieces that each fit, and let the destination host reassemble them. This matters because the internet is not one link. It is a concatenation of technologies built at different times for different purposes. These are glued together by routers that know nothing about each other's link-layer limits. A packet from a modern data center might cross a legacy tunnel, a satellite hop, and a home Wi-Fi network before landing. Without fragmentation (or the closely related mechanism of path MTU discovery), any datagram larger than the smallest MTU on the path would simply be undeliverable. Fragmentation is the fallback that keeps delivery possible even when senders guess wrong about link limits. A second, seemingly unrelated field in the IP header — Time to Live (TTL) — solves a completely different but equally fundamental problem. What happens when routing tables are momentarily inconsistent and a packet starts circulating in a loop? Without a hard stop, transient routing loops would let packets loop forever. Such loops are unavoidable in any distributed, asynchronously-updated system. They would consume bandwidth and CPU cycles indefinitely. TTL is IP's simple, decrementing safety valve. It guarantees that every datagram eventually dies. This article explains, precisely and mechanically, how a datagram gets split at a router. It also explains how it gets reassembled at the destination. It shows how the Identification, Flags, and Fragment Offset fields cooperate. Together they make that safe even with reordering, duplication, and loss. It then explains TTL: why 8 bits are enough, and how it interacts with ICMP. It shows how the two mechanisms — fragmentation and TTL — together keep IP delivery both flexible and bounded. It closes by setting up why IPv6 changed both stories, foreshadowed in IPv6: Motivation and Datagram Format.

Key takeaways
  • IP fragmentation splits an oversized datagram into pieces no larger than the outgoing link's MTU, using Identification, Flags, and Fragment Offset header fields.
  • Reassembly happens only at the final destination, never at intermediate routers, using the Identification field to group fragments and the Offset field to reorder them.
  • The More Fragments (MF) flag marks all but the last fragment; Don't Fragment (DF) forbids splitting and instead triggers an ICMP error, enabling Path MTU Discovery.
  • Fragment Offset is measured in 8-byte units, forcing all fragment payload sizes (except the last) to be multiples of 8.
  • TTL (Time to Live) is decremented by every router; a datagram whose TTL reaches zero is discarded, guaranteeing termination despite routing loops.
  • TTL exhaustion triggers an ICMP Time Exceeded message back to the sender, the mechanism exploited by the `traceroute` tool.
The core insight

IP fragmentation lets a single datagram survive a path of mismatched link capacities by splitting into self-describing pieces, while TTL guarantees that no datagram — fragmented or not — can survive forever in a network with transient routing loops.

The idea

An IP datagram is opaque to the routers that forward it, except for its header. When a router must forward a datagram out an interface whose Maximum Transmission Unit (MTU) is smaller than the datagram's total length, it has two choices: drop the datagram (if fragmentation is disallowed) or split it into multiple smaller datagrams called fragments. Each fragment is a fully valid IP datagram in its own right, with its own header, capable of being routed independently and possibly arriving out of order, delayed, or duplicated — exactly like any other datagram. Four IP header fields make fragmentation and reassembly possible. The Identification (ID) field (16 bits) is a number chosen by the original sender for that datagram; every fragment produced from it carries the same ID, so the destination knows which fragments belong together. The Flags field includes the DF (Don't Fragment) bit, which forbids any router from fragmenting the datagram (used for Path MTU Discovery), and the MF (More Fragments) bit, set on every fragment except the last one, telling the receiver when the group is complete. The Fragment Offset field (13 bits) records where, in units of 8 bytes, this fragment's payload belongs within the original datagram's data stream. Because reassembly is stateless and can be interleaved with fragments of other datagrams to the same host, the tuple (source address, destination address, protocol, Identification) uniquely identifies a fragment group. A crucial design decision is that fragmentation can happen at any router along the path, but reassembly happens only at the final destination host (never at an intermediate router). This asymmetry is deliberate: different fragments of the same datagram may take different routes and arrive at different times, so only the endpoint that owns the full picture — the destination — can safely wait for and reorder them. This design keeps routers stateless and fast, at the cost of pushing complexity (timers, buffer management, and vulnerability to incomplete-fragment attacks) to the destination.

How it works

Fragmentation at a router. When a router must send a datagram of length `L` (header + data) out a link with MTU `M`, and `L > M`, it fragments as follows. The datagram's header itself is typically 20 bytes (no options, the common case); data must be split into chunks whose length is a multiple of 8 bytes (except possibly the last chunk), because Fragment Offset counts 8-byte units. ``` function FRAGMENT(datagram, MTU): header = datagram.header // 20 bytes, no options (common case) headerLen = length(header) data = datagram.payload maxDataPerFragment = floor((MTU - headerLen) / 8) 8 // must be multiple of 8 if maxDataPerFragment <= 0: error "MTU too small to carry any data" fragments = [] offsetBytes = 0 originalMF = datagram.flags.MF // usually 0 unless already a fragment originalDF = datagram.flags.DF if originalDF == 1: // Not allowed to fragment: drop and report send ICMP "Destination Unreachable: Fragmentation Needed" to source return [] while offsetBytes < length(data): chunk = data[offsetBytes : offsetBytes + maxDataPerFragment] // last chunk may be shorter newHeader = copy(header) newHeader.totalLength = headerLen + length(chunk) newHeader.identification = header.identification // SAME for all fragments newHeader.fragmentOffset = offsetBytes / 8 // in 8-byte units isLast = (offsetBytes + length(chunk) >= length(data)) newHeader.flags.MF = isLast ? originalMF : 1 // set MF unless this is the last piece newHeader.flags.DF = originalDF newHeader.checksum = computeChecksum(newHeader) // recompute! header changed fragments.append(makeDatagram(newHeader, chunk)) offsetBytes += length(chunk) return fragments ``` Two subtleties matter for correctness. First, the header checksum must be <strong>recomputed</strong> for each fragment, since the total length and offset fields changed. Second, if a datagram that is already a fragment (MF = 1, offset > 0) itself needs further fragmentation, the new sub-fragments inherit the original's MF status on the last piece and set MF = 1 on all earlier pieces — the offset field is always relative to the original* unfragmented datagram, so nested fragmentation composes correctly by just adding offsets.

Reassembly at the destination. The destination host buffers arriving fragments, keyed by (source, destination, protocol, ID), until it has enough information to reconstruct the original payload. ``` function REASSEMBLE(fragment, buffers, timers, TIMEOUT): key = (fragment.src, fragment.dst, fragment.protocol, fragment.identification) if key not in buffers: buffers[key] = new FragmentBuffer() start timer for key with duration TIMEOUT buf = buffers[key] buf.insert(fragment.offset 8, fragment.data) // place data at byte offset buf.receivedRanges.add([fragment.offset8, fragment.offset8 + length(fragment.data)]) if fragment.flags.MF == 0: buf.totalLength = fragment.offset8 + length(fragment.data) // now we know final size if buf.totalLength is known AND buf.receivedRanges covers [0, buf.totalLength): cancel timer for key originalDatagram = buf.header + buf.assembledData remove buffers[key] deliver originalDatagram to upper layer // else: keep waiting for more fragments function ON_TIMEOUT(key, buffers): if key in buffers: discard buffers[key] // incomplete: drop silently or ICMP optionally send ICMP "Time Exceeded (fragment reassembly)" ``` The receiver does not need fragments to arrive in order. It tracks which byte ranges of the original payload it has received (`receivedRanges`), and it only learns the total length of the datagram once it sees the fragment with MF = 0 (the last one), because that fragment's offset plus its length gives the true total. Reassembly completes exactly when the union of received ranges covers `[0, totalLength)` with no gaps.

The TTL field and loop prevention. Independently of fragmentation, every IP header carries an 8-bit TTL (Time to Live) field, set by the sender (commonly to 64, 128, or 255 depending on the OS). Each router that forwards the datagram decrements TTL by (at least) one before forwarding. ``` function FORWARD(datagram): datagram.TTL = datagram.TTL - 1 if datagram.TTL <= 0: send ICMP "Time Exceeded (TTL expired in transit)" to source drop datagram return recompute IP header checksum // TTL changed, checksum must be updated lookup next hop and send datagram ``` TTL was originally meant to represent seconds a datagram may live (hence its name), with routers that hold a packet for multiple seconds supposed to decrement by more than one. In practice virtually all routers decrement by exactly one per hop, so TTL functions as a hop-count limit. This guarantees that a datagram traversing a routing loop is destroyed after at most `initial TTL` hops, no matter how long the loop persists.

Complexity & correctness

Fragmentation cost. Splitting a datagram of data length `D` for a link with usable payload capacity `C` (a multiple of 8) produces ``` ⌈D / C⌉ ``` fragments, each requiring O(1) header construction and a checksum recomputation over a 20-byte header — O(1) time per fragment, so O(⌈D/C⌉) time and space for the fragmenting router. This is optimal: any correct scheme must emit at least `⌈D/C⌉` fragments to cover `D` bytes and cannot do so in fewer packets, since each packet carries at most `C` data bytes.

Reassembly cost. The destination must buffer up to the full datagram size, `O(D)` space, and track received byte ranges. Using an interval-list or bitmap representation, insertion and gap-checking cost `O(k)` per fragment (where `k` is the number of previously-received fragments, using a sorted interval list) or `O(1)` amortized with a bitmap over 8-byte units, giving O(D/8) total space for the coverage bitmap and O(⌈D/C⌉) total fragments processed, each O(1) with a bitmap — so reassembly is O(D) time and space overall, linear in the datagram size, which is asymptotically necessary since every byte must be written somewhere.

Correctness of reassembly. The key invariant is: A fragment group is complete if and only if the union of `[offset·8, offset·8 + len)` ranges over all received fragments with the same key equals `[0, totalLength)`, where `totalLength` is determined by the unique fragment with MF = 0. This holds because (1) fragmentation partitions `[0, D)` into contiguous, non-overlapping ranges by construction (the `FRAGMENT` procedure advances `offsetBytes` by exactly `length(chunk)` each iteration with no gaps or overlaps), and (2) exactly one fragment — the last one produced — has MF = 0, so `totalLength` is well-defined once that fragment arrives. Since IP fragments can be duplicated (retransmitted by a lower layer or replayed), the buffer must treat overlapping/duplicate ranges idempotently — inserting the same range twice must not create false gaps or double-counted coverage, which the bitmap/interval-union approach naturally guarantees. The reassembly timer bounds how long a receiver waits for a straggling fragment (commonly 30–60 seconds), after which the incomplete datagram is discarded — this is necessary because otherwise a single lost fragment could leak buffer memory forever, a real denial-of-service vector.

Correctness of TTL-based loop termination. Claim: with initial TTL value `T`, no datagram survives more than `T` hops before being dropped. Proof: each hop strictly decreases TTL by at least 1 (an invariant enforced by every conformant router), and a datagram is dropped as soon as TTL would reach 0 or below. TTL is a non-negative integer that strictly decreases at each hop, so by the well-ordering of the naturals it must reach the drop threshold within `T` steps — this is precisely a decreasing-measure / well-founded-descent argument, the same style of reasoning used to prove loop termination in program verification. This holds regardless of the routing table's correctness: even if routing entries form a cycle (a transient inconsistency during convergence, addressed by protocols discussed alongside distance-vector and link-state routing), the datagram cannot loop indefinitely — it is guaranteed to expire.

Tradeoffs. Fragmentation solves the MTU-mismatch problem but at real cost: if even one fragment is lost, the entire original datagram must be retransmitted (since transport-layer retransmission, e.g., TCP, operates on whole segments, not fragments), amplifying the impact of loss. Fragmentation also burdens the destination with reassembly state and exposes it to attacks (see below). This is why modern practice strongly prefers Path MTU Discovery — sending with DF set and shrinking segment size in response to ICMP "Fragmentation Needed" errors — over relying on in-network fragmentation, and why IPv6 removes router fragmentation entirely, pushing the responsibility to the sending host.

Worked example

A host sends a UDP datagram carrying 3000 bytes of application data. The IP header is 20 bytes (no options), so total datagram length is 3020 bytes, Identification = 512, DF = 0, MF = 0, offset = 0. It travels over Ethernet (MTU 1500) to a router R, which must forward it onto a PPP link with MTU 620. Usable payload per fragment: `⌊(620 − 20)/8⌋ × 8 = ⌊600/8⌋×8 = 75×8 = 600` bytes. Number of fragments: `⌈3000 / 600⌉ = 5`.

FragmentOffset field (8-byte units)Byte range in original dataFragment total lengthMF
100–59920+600 = 6201
275600–11996201
31501200–17996201
42251800–23996201
53002400–29996200

All five fragments carry Identification = 512, and each has its header checksum recomputed independently. Suppose fragment 3 is dropped by a congested queue downstream, but fragments 1, 2, 4, 5 arrive at the destination. The receiver's buffer for key `(src, dst, UDP, 512)` accumulates coverage `[0,600) ∪ [600,1200) ∪ [1800,2400) ∪ [2400,3000)`, i.e., `[0,1200) ∪ [1800,3000)` — a gap at `[1200,1800)`. Because fragment 5 (MF=0) arrived, `totalLength = 3000` is known, but coverage never reaches `[0,3000)`. After the reassembly timeout expires, the receiver discards all four buffered fragments and — because this was the first fragment's range that's missing is not the case here, but generally if fragment offset 0 was received — may send an ICMP "Time Exceeded (reassembly)" message back to the original sender. The entire 3000-byte UDP datagram is lost, even though 80% of its bytes arrived intact — illustrating the amplification-of-loss tradeoff discussed above. Now trace TTL on the same path. Suppose the sender set TTL = 4, and the path is Host → R1 → R2 → R3 → Destination (3 router hops). Each router decrements TTL by 1: it arrives at R1 with TTL 4→3, at R2 with TTL 3→2, at R3 with TTL 2→1, and reaches the destination with TTL 1 (still positive, since destinations don't forward, only routers decrement-and-forward). Delivery succeeds. If instead a routing misconfiguration created a loop R2→R3→R2, a datagram entering that loop with TTL = 4 would be decremented at each pass — 4→3 at R2, 3→2 at R3, 2→1 at R2 again, 1→0 at R3 again — and R3 would drop it and emit an ICMP Time Exceeded message rather than looping forever.

Common pitfalls & traps

PitfallWhy it happensConsequence
Forgetting fragment offset is in 8-byte units, not 1-byteField is only 13 bits; can't address byte offsets up to 65535 directlyOff-by-factor-of-8 bugs when computing byte ranges from the offset field
Not recomputing the header checksum per fragmentCopying the original header naivelyFragments silently corrupted/dropped by checksum-verifying routers or hosts
Assuming all fragments must arrive in orderConfusing IP (connectionless, unordered) with TCP segment orderingReassembly code that fails on out-of-order arrival
Treating loss of any one fragment as recoverable at IP layerIP has no per-fragment retransmissionEntire datagram is lost; must rely on transport-layer retransmission of the whole segment
Ignoring the DF bit and fragmenting anywayLegacy or misconfigured routersBreaks Path MTU Discovery, silently corrupts DF-protected traffic (e.g., some VPN tunnels)
Using TTL=1 or very low TTL for "normal" trafficConfusing TTL's loop-prevention role with a delivery guaranteeDatagrams die a few hops from origin, seen as intermittent unreachability
Never implementing a reassembly timeoutUnderestimating adversarial or lossy conditionsUnbounded buffer growth — a resource-exhaustion (DoS) vulnerability

Pioneers & history

The mechanisms described here were codified in the foundational specification of the Internet Protocol, refined through the 1970s and early 1980s as the ARPANET's homogeneous link assumptions gave way to a genuinely heterogeneous internetwork of Ethernet, X.25, packet radio, and satellite links — each with a different MTU.

Origins · 1 minJonathan B. Postel and how this was discovered
JB
Jonathan B. Postel
1943–1998 · Internet protocol architecture and standardization

Jon Postel edited and shepherded the core Internet Protocol specifications through the Internet Engineering process, serving for decades as RFC editor and the de facto authority coordinating how protocols like IP were defined, revised, and adopted across a rapidly diversifying set of physical networks.

History
1974
— Vint Cerf and Robert Kahn publish the TCP design later split into TCP and IP, motivating a datagram format robust to heterogeneous link technologies.
1980–1981
— IP is formalized with the Identification, Flags, Fragment Offset, and Time to Live fields defined essentially as used today.
1980
s — Growing internetworks with wildly varying link MTUs (Ethernet, X.25, ARPANET IMP links) make router-level fragmentation a practical necessity.
1990
s — Path MTU Discovery (using the DF bit and ICMP Fragmentation Needed messages) becomes preferred practice, reducing reliance on in-network fragmentation.
1998
— IPv6 specification removes router fragmentation entirely, requiring source hosts to perform Path MTU Discovery and fragment before sending, foreshadowing the redesign covered next.

Where this matters

Fragmentation and TTL are load-bearing mechanisms across the entire internet infrastructure, not historical curiosities. DNS over UDP occasionally produces responses large enough to require fragmentation (especially with DNSSEC), which is one reason large deployments push toward TCP fallback or EDNS0 buffer-size negotiation to avoid fragmentation-related failures. VPN and tunneling protocols (IPsec, GRE, VXLAN) routinely encounter reduced effective MTU because of added encapsulation headers, making correct handling of DF, fragmentation, and ICMP "Fragmentation Needed" messages essential for tunnel reliability — misconfigured firewalls that block that ICMP message are a classic, hard-to-diagnose cause of "traffic hangs only for large packets." TTL underlies the ubiquitous `traceroute`/`tracert` diagnostic tool, which sends probes with increasing TTL values (1, 2, 3, …) specifically to trigger ICMP Time Exceeded responses from each successive router, mapping the path hop by hop. TTL also functions as a coarse anti-amplification and scope-limiting mechanism in multicast routing (TTL scoping) and is checked by security-conscious middleboxes to detect spoofed or malformed traffic. Content-delivery networks and load balancers monitor TTL to infer approximate network distance, and DDoS-mitigation appliances use anomalous TTL patterns as one signal among many for spoofed-source detection. Understanding fragmentation and TTL together explains real, frequently-debugged phenomena — "large packets fail, small ones work," "traceroute stops partway," "one router seems to eat half my traffic" — that recur across enterprise networking, cloud infrastructure, and internet-scale operations.

Ask the tutor: How does Path MTU Discovery actually recover when the ICMP "Fragmentation Needed" message itself gets blocked by a firewall?

Why don't routers reassemble fragments before forwarding them further?

Reassembly requires buffering all fragments of a datagram and waiting, potentially, for the slowest one to arrive — this is inherently stateful and can take a long time if a fragment is delayed or lost. Routers are designed to be fast, largely stateless, per-packet forwarding devices; adding mandatory buffering and timers for every fragmented flow would hurt throughput and create memory-exhaustion risk at high-traffic aggregation points. Additionally, fragments belonging to the same original datagram may legitimately take different paths through the network (since IP routing is per-packet, not per-flow, in general), so an intermediate router might never even see all the fragments of a given datagram, making in-network reassembly not just costly but sometimes impossible. Pushing reassembly to the endpoint, which is guaranteed to be the single destination for all fragments, is both simpler and universally correct.

What is Path MTU Discovery and how does it relate to the DF bit?

Path MTU Discovery (PMTUD) is a technique where the sending host sets the DF (Don't Fragment) bit on outgoing datagrams and starts with an optimistic size, often the local link's MTU. If a router along the path cannot forward the datagram without fragmenting it, and DF is set, the router drops the datagram and returns an ICMP "Destination Unreachable: Fragmentation Needed" message, ideally including the MTU of the bottleneck link. The sender reduces its datagram size accordingly and retries, iterating until no more ICMP errors arrive, at which point it has discovered the minimum MTU along the entire path. This avoids in-network fragmentation entirely, at the cost of requiring that ICMP error messages actually reach the sender — a requirement broken by firewalls that block ICMP, causing the well-known "PMTUD black hole" failure mode.

Why is TTL 8 bits, and why do operating systems pick values like 64, 128, or 255?

Eight bits (0–255) was judged sufficient because no realistic internet path has anywhere near 255 router hops; typical paths span roughly 10–30 hops. Different operating systems historically chose different default initial TTLs — Linux commonly defaults to 64, Windows to 128, and some older Unix or networking equipment to 255 — largely as a legacy convention rather than a strict technical requirement. These differing defaults are sometimes used for lightweight OS fingerprinting, since the TTL observed on an arriving packet (after subtracting the estimated number of hops) can hint at the sender's operating system family, though this is a heuristic, not a reliable identifier.

Can two different datagrams from the same host ever be confused during reassembly if they share the same Identification value?

The Identification field alone is not the full key; reassembly uses the tuple of source address, destination address, protocol number, and Identification together. Since Identification is typically a 16-bit counter that wraps around, it is possible in principle for two datagrams between the same host pair using the same protocol to reuse an Identification value, especially under very high packet rates or with poorly randomized ID selection. This was a known weakness exploited in some historical attacks (predictable IDs enabling packet injection or fragment overlap attacks), which is one reason modern stacks randomize Identification values rather than using simple sequential counters, and why reassembly timeouts are kept relatively short to reduce the window in which a wrapped ID could cause ambiguity.

Does fragmentation affect performance even when it succeeds completely?

Yes. Fragmentation multiplies the number of packets that must be processed by every router and the destination host, increasing per-packet overhead (header processing, interrupt handling, buffer allocation) relative to the useful payload carried. It also means the loss probability of the entire original datagram is higher than the loss probability of any single packet, because losing any one fragment dooms the whole set — for `n` fragments each with independent loss probability `p`, the datagram-loss probability is approximately `1 − (1−p)^n`, which grows with `n`. For these reasons, protocol designers and operators strongly prefer avoiding fragmentation via correct MTU discovery over relying on it as a routine mechanism.

How does TTL differ conceptually from a retry limit or a TCP retransmission timeout?

TTL bounds how many times a single datagram can be forwarded by routers, addressing loops and runaway propagation at the network layer, entirely independent of any higher-layer retransmission logic. A TCP retransmission timeout, by contrast, governs when the transport layer at the sending host decides a segment was lost and should be resent as a new datagram (with a fresh TTL). The two mechanisms operate at different layers and timescales: TTL is decremented hop-by-hop within microseconds to milliseconds as a single datagram crosses the network, while TCP retransmission timers operate on the order of the connection's round-trip time, deciding whether to inject an entirely new datagram into the network.

What replaces router-based fragmentation in IPv6, and why?

IPv6 removes the ability for routers to fragment datagrams in transit entirely; only the originating host may fragment, and only before sending, using an IPv6-specific extension header. This shift, motivated by the goal of simplifying router processing (fixed, streamlined IPv6 headers avoid variable-length fragmentation fields in the base header) and by the maturity of Path MTU Discovery as standard practice, pushes all MTU-adaptation responsibility to the sending host. The tradeoff and full mechanics of this redesign are the natural next topic once router-based IPv4 fragmentation, as described here, is well understood.

Before this

Where this leads