MTU and Fragmentation

A network link is not a smooth pipe you can pour any amount into at once. Every link has a maximum frame size it will carry, and the moment your data exceeds it something has to give. On the Ethernet that dominates the world, that limit — the Maximum Transmission Unit (MTU) — is famously 1500 bytes. Try to send a 4000-byte IP packet across it and the network layer must chop your packet into pieces small enough to fit, ship them separately, and glue them back together at the far end. That chopping-and-gluing is fragmentation, and it is one of those quiet mechanisms that mostly works — until it spectacularly doesn't.

This page is about the MTU, how IPv4 fragments a too-big packet (and why that is more costly and fragile than it looks), how Path MTU Discovery lets a sender find the largest size that will survive end-to-end without any fragmentation at all, and how it all ties back to TCP's MSS and the UDP datagrams you send by hand. Understanding MTU is the difference between a network that "mysteriously hangs on large transfers" and one you can actually reason about.

The MTU: how big is one frame?

The MTU is the largest payload a link-layer frame will carry — i.e. the biggest IP packet that fits in one frame without splitting. It is a property of the link, not the packet, so different hops on a path can have different MTUs, and the one that matters is the smallest along the whole path (the path MTU).

Link / contextMTU (bytes)
Classic Ethernet1500
Ethernet "jumbo frames" (data-centre)9000
PPPoE (DSL) — 8 bytes of overhead1492
IPv6 minimum every link must support1280
Common "safe" internet size (tunnels, VPNs)~1400 or 1200

Note that tunnels and VPNs (IPsec, WireGuard, GRE) wrap your packet in extra headers, eating into the 1500 and lowering the effective MTU — which is exactly why "it works normally but big transfers over the VPN stall" is such a common, maddening symptom.

How IPv4 fragments a too-big packet

Suppose a host wants to send 4000 bytes of data in one IP packet, but the outgoing link has MTU 1500. The IPv4 header is 20 bytes, so at most 1500 - 20 = 1480 bytes of data fit per fragment — and because the fragment offset is measured in 8-byte units, each non-final fragment must carry a multiple of 8 bytes (1480 is, conveniently, 185 \times 8). So the 4000 bytes split into three fragments:

Three header fields make reassembly work, all in the IPv4 header:

Crucially, reassembly happens only at the final destination — never at intermediate routers — because the fragments may take different paths and no single router is guaranteed to see them all.

// Fragment an IPv4 datagram: given payload size and link MTU, compute each // fragment's byte range, 8-byte offset unit, and More-Fragments flag. // Also derive the TCP MSS that AVOIDS fragmentation on this link. const IP_HDR = 20, TCP_HDR = 20; function fragment(dataLen: number, mtu: number) { const maxData = Math.floor((mtu - IP_HDR) / 8) * 8; // multiple of 8 const frags: string[] = []; let off = 0, i = 1; while (off < dataLen) { const len = Math.min(maxData, dataLen - off); const mf = off + len < dataLen ? 1 : 0; frags.push( `Frag ${i}: bytes ${off}-${off + len - 1} ` + `offset=${off / 8} MF=${mf} (${len} data bytes)`, ); off += len; i++; } return frags; } for (const line of fragment(4000, 1500)) console.log(line); const mss = 1500 - IP_HDR - TCP_HDR; console.log(`\nTCP MSS on a 1500 MTU link = 1500 - ${IP_HDR} - ${TCP_HDR} = ${mss} bytes`); console.log("Send segments of MSS or less and IP never has to fragment them.");

Why fragmentation is something to avoid

Fragmentation works, but every seasoned network engineer treats it as a code smell. The reasons stack up:

IPv6 took the hint and banned router fragmentation entirely. In IPv6 an intermediate router that finds a packet too big does not fragment it — it drops it and sends back an ICMPv6 Packet Too Big message. Only the original source may fragment, and it is expected to avoid even that by discovering the path MTU first. The whole ecosystem now leans on finding the right size before sending, not chopping up after.

Path MTU Discovery: find the size that fits

The elegant fix is to never send anything too big in the first place. Path MTU Discovery (PMTUD) lets a sender probe for the largest size that survives the whole path:

This is why a healthy connection sends full-1500-byte packets on a local network but automatically drops to, say, 1400 across a tunnel — PMTUD found the ceiling. TCP then advertises its MSS to keep its segments under it: \text{MSS} = \text{MTU} - 20\,(\text{IP}) - 20\,(\text{TCP}) = 1460 on a 1500-byte link. UDP has no such helper — a UDP application must keep its own datagrams small (a common safe target is ~1200 bytes for internet paths), because if it oversends, IP fragmentation with all its fragility is the only thing catching it.

PMTUD has a notorious failure mode. It depends entirely on those ICMP "Fragmentation Needed" messages getting back to the sender. But nervous administrators, thinking "ICMP is just ping, and ping is a security risk," block all ICMP at their firewall. Now the sequence is deadly: the sender fires off a 1500-byte packet with DF set; a downstream router drops it for being too big and dutifully sends the ICMP notification — which is filtered before it arrives. The sender hears nothing, assumes the packet was merely lost, and retransmits it at the same size, forever.

The result is a PMTUD black hole: the connection completes its handshake fine (small packets get through), then hangs the instant it tries to send a full-size packet — the classic "the page starts loading then freezes" or "SSH connects but ls of a big directory hangs." The lesson is blunt: do not blindly block all ICMP. The "Fragmentation Needed" and "Packet Too Big" messages are load-bearing; without them, PMTUD silently breaks. (Modern stacks add PLPMTUD, RFC 8899, which probes with different sizes and needs no ICMP as a fallback.)

Every packet costs fixed overhead: 20+ bytes of headers, interrupts, and per-packet CPU work. On a storage or backup network moving huge streams, a 1500-byte MTU means chopping a gigabyte into ~700,000 packets, each paying that tax. Bump the MTU to 9000 (a jumbo frame) and you send the same data in ~117,000 packets — roughly a sixth of the per-packet overhead, noticeably less CPU, and higher throughput. The catch is that every device on that layer-2 segment must agree on 9000, or you get a mismatched-MTU mess. So jumbo frames live in controlled data-centre fabrics, not on the open internet, where you can't assume the whole path supports them.