ICMP: Explanation & Insights

The channel the network uses to talk about itself — errors, diagnostics, and the occasional "you can't get there."

What It Is

Most of what crosses a network is content: a web page, a database query, a video frame. ICMP is the exception. The Internet Control Message Protocol carries no application data at all. It's the network's own back-channel — the layer that lets routers and hosts send each other small status notes about the traffic they're handling. "This packet's TTL ran out." "There's no route to that host." "Your packet is too big to fit through here." It's the protocol the internet uses to talk about the other protocols.

On the OSI model, ICMP is a curious resident. It rides inside IP packets, just like TCP and UDP do — so by encapsulation it looks like a layer-4 passenger. But it isn't a transport for your data; it exists to manage the network layer itself, and the official line is that it's an integral part of IP. Don't lose sleep over the taxonomy. The useful model is simpler: when something goes wrong with an IP packet, or a tool wants to probe the path, ICMP is the messenger.

You meet it constantly without naming it. Every ping is ICMP. Every map of hops that traceroute draws is built entirely from ICMP replies. And every time a large download mysteriously stalls while small pages load fine, there's a good chance a missing ICMP message is the culprit — the muddy, costly thing this page exists to make clear.

(A note on names: there are two ICMPs. The classic ICMPv4 goes with IPv4; ICMPv6 goes with IPv6 and does even more. The concepts below apply to both; only the type numbers differ. We use the v4 names — what you'll see most on a typical Linux box.)

How It Works

An ICMP message is tiny: a one-byte type, a one-byte code (a sub-flavour of the type), a checksum, and a payload that depends on the type. There are no ports — ICMP doesn't have them, which matters later when we talk about NAT and firewalls. The whole thing sits inside an IP packet like a letter inside an envelope, and the IP header's protocol field is set to 1 to say "this one's ICMP."

A handful of types carry almost all the weight. Learn these four and you understand 95% of ICMP in the wild.

Echo Request and Echo Reply — the heartbeat

This is the famous pair. Type 8 is Echo Request: "are you there?" Type 0 is Echo Reply: "yes, here's your data bounced straight back." When you run ping, your host sends Echo Requests with a sequence number and a timestamp; the target copies the payload verbatim into an Echo Reply and sends it home. The round-trip time you see is just the gap between sending and getting your own bytes back.

ping -c 3 example.com
PING example.com (93.184.216.34) 56(84) bytes of data.
64 bytes from 93.184.216.34: icmp_seq=1 ttl=56 time=11.4 ms
64 bytes from 93.184.216.34: icmp_seq=2 ttl=56 time=11.2 ms
64 bytes from 93.184.216.34: icmp_seq=3 ttl=56 time=11.3 ms

That's it — the entire ping protocol. The reason ping is the first reflex of every admin who's ever debugged a network is that it tests the whole path in one shot: your NIC, your routes, NAT, the wider internet, and the remote host's willingness to answer, all confirmed by a single returned packet.

Time Exceeded — how traceroute sees the map

Every IP packet carries a TTL (Time To Live), a small counter that every router decrements by one as it forwards the packet. When the TTL hits zero, the router throws the packet away and sends back an ICMP Time Exceeded (type 11). The TTL exists to stop packets from looping the internet forever — a routing mistake that would otherwise create immortal traffic.

traceroute turns that safety mechanism into a mapmaking tool, and it's one of the loveliest hacks in all of networking. It sends a packet with TTL=1. The very first router decrements it to zero and replies with Time Exceeded — revealing its own address. Then TTL=2: the second router replies. TTL=3, TTL=4, and so on, walking the packet one hop further each round until it finally reaches the destination. Each hop is forced to announce itself by the act of expiring a packet on purpose.

traceroute example.com
 1  192.168.1.1   0.4 ms
 2  10.0.0.1      3.2 ms
 3  * * *
 4  93.184.216.34 11.5 ms

The * * * on hop 3 is a router that declined to send Time Exceeded (or whose replies were filtered) — common, harmless, and a hint that not everyone on the path is chatty.

Destination Unreachable — the polite refusal

Type 3, Destination Unreachable, is ICMP's way of saying "that didn't work, and here's why." The code tells you which kind of failure:

  • Network unreachable (code 0) — no route to that whole network exists.
  • Host unreachable (code 1) — the network's reachable but the specific host isn't answering.
  • Port unreachable (code 3) — the host is up, but nothing is listening on that UDP port. This is how traceroute knows it has arrived: it aims at a closed high port, and the Port Unreachable reply is the destination's way of saying "you've reached me."
  • Fragmentation Needed (code 4) — the most important code on this page, and the one that breaks the internet quietly when you suppress it. Next section.

Fragmentation Needed and Path MTU Discovery

Every link has a MTU — a Maximum Transmission Unit, the largest packet it will carry in one piece. Ethernet's is famously 1500 bytes; a VPN or tunnel shaves off a little for its own headers, so its MTU might be 1400 or less. The trouble is your two endpoints don't know about that narrow link in the middle. So how does a sender pick a packet size that survives the whole journey?

The modern answer is Path MTU Discovery (PMTUD), and it leans entirely on one ICMP message. The sender marks its TCP packets with the Don't Fragment bit — "do not chop this up." If that packet meets a link too small to carry it, the router can't fragment it (forbidden) and can't forward it (too big), so it does the only thing left: drops the packet and sends back ICMP Fragmentation Needed (type 3, code 4) — including the MTU of the narrow link. The sender reads that number, shrinks its packets to fit, and retransmits. The path's true MTU is discovered the hard way, one bounced packet at a time, exactly the way traceroute discovers hops.

It's an elegant feedback loop — and it has one fatal dependency. If that single ICMP message never arrives, the sender never learns to shrink. It keeps firing oversized packets into a link that silently swallows every one. Hold that thought; it's the whole point of the next section.

The Mistake That Breaks Things Quietly

Here is the single most expensive misconception about ICMP, and you will see it in the wild on real production servers:

Warning

"Block all ICMP — it's a security risk." This advice is everywhere, it sounds prudent, and it is wrong. A blanket ICMP block doesn't harden your server so much as blindfold your network. The damage is delayed and maddening to diagnose, because the thing it breaks is invisible until someone tries to move a large amount of data.

The reasoning isn't crazy. ICMP can be abused — ping floods, smurf attacks, the old "ping of death," and Echo probing to map a network's live hosts. So a firewall admin reaches for the big hammer and drops everything with protocol 1. Threat addressed, box closed.

Except they just murdered Path MTU Discovery. Recall the chain: a large TCP transfer hits a small link, the router sends Fragmentation Needed, the sender shrinks. Drop ICMP and that message dies at your firewall. Now you have the classic, infuriating signature of an MTU black hole:

  • Small things work perfectly. SSH logs in. ping succeeds (small packets fit everywhere). Short web pages load. Everything feels fine.
  • Large things hang forever. A file download stalls at a few kilobytes. A big HTTPS response never completes. An scp freezes mid-transfer. A TLS handshake with a long certificate chain times out.

The packets that fit sail through; the ones that don't are dropped at the narrow link, and the "please shrink" message that would have fixed it is sitting in your firewall's deny log. The connection isn't refused — it dies of silence. People burn whole afternoons on this, blaming the app, the disk, the bandwidth, anything but the firewall rule three months old that nobody remembers writing.

The fix is to stop blanket-blocking ICMP and instead allow the types that matter. Specifically:

  • Always allow Fragmentation Needed (type 3, code 4). This is non-negotiable. Without it, PMTUD is dead and large transfers black-hole. It is never the right call to drop this one.
  • Allow the rest of Destination Unreachable and Time Exceeded. These make traceroute work and let your apps fail fast with a clean error instead of a long timeout.
  • Allow Echo Request, or rate-limit it. A server that answers ping is a server you can monitor and debug. If you're worried about floods, rate-limit it (a few packets per second is plenty), don't drop it. The "stealth by not answering ping" posture buys you almost nothing — a real scanner just sends a TCP SYN to port 443 — while costing you a universally useful diagnostic.

Pro Tip

If you must rate-limit rather than fully allow, do it with the protocol's own knobs. On Linux, iptables can cap Echo Requests with -p icmp --icmp-type echo-request -m limit --limit 5/s and then accept Fragmentation-Needed unconditionally above it. The order matters: never let a broad ICMP-drop rule shadow the line that permits Packet-Too-Big.

One second-order subtlety. Because ICMP has no ports, NAT has to work harder to deliver these errors to the right internal host: a good NAT box reads the quoted original packet inside the ICMP error to match it to a connection, then forwards it. A cheap one drops ICMP errors on the floor — recreating the exact PMTUD black hole from inside your own gateway. So even when your firewall is innocent, your NAT can be the silent killer.

How I Inspect It

When ICMP is the suspect, I watch it directly. tcpdump speaks ICMP fluently, and a filter on icmp shows you the back-channel in real time:

tcpdump -ni eth0 icmp
IP 10.0.0.5 > 93.184.216.34: ICMP echo request, id 4321, seq 1
IP 93.184.216.34 > 10.0.0.5: ICMP echo reply, id 4321, seq 1
IP 10.0.0.1 > 10.0.0.5: ICMP 93.184.216.34 unreachable - need to frag (mtu 1400)

That last line is the gold. need to frag (mtu 1400) is Path MTU Discovery working as designed — a router telling your host the path tops out at 1400 bytes. If you're chasing an MTU black hole, the absence of that line during a stalled transfer is the diagnosis: the message you need isn't coming back.

A quick way to prove an MTU problem without a packet trace is to ping with the Don't-Fragment bit set and a payload sized to probe the link:

ping -M do -s 1472 example.com   # 1472 + 28 bytes header = 1500

If 1472 fails but a smaller size succeeds, you've found a link with an MTU below 1500 — and confirmed that something on the path needs to be allowed to tell you so. Pair this with a look at your own nic MTU (ip link show) and your iptables rules, and the picture usually resolves fast.

Cheat Sheet

The ICMP types you'll actually meet, and what each one means when it lands.

Type Code Name What it tells you
0 0 Echo Reply "I'm here" — the answer to your ping.
3 0 Net Unreachable No route to that whole network.
3 1 Host Unreachable Network's fine; that host isn't answering.
3 3 Port Unreachable Host's up, nothing listening on that UDP port.
3 4 Fragmentation Needed Packet too big — shrink to this MTU. Never block this.
8 0 Echo Request "Are you there?" — what ping sends.
11 0 Time Exceeded TTL hit zero — the engine behind traceroute.
5 Redirect "Use a better gateway" — often disabled for security; that's fine.

Redirect (type 5) is the one type it is reasonable to ignore or drop — it lets a router rewrite your routing table on the fly, which is more attack surface than convenience on a server with sane static routes. Block that one if you like. Just don't take the rest down with it.

See Also

  • protocol — the layered stack ICMP rides inside and reports on
  • OSI model — where ICMP awkwardly but usefully sits
  • TCP — the transport whose large transfers depend on PMTUD working
  • UDP — the connectionless cousin; its Port Unreachable is how traceroute lands
  • NAT — why portless ICMP errors are tricky to route home
  • NIC — where MTU lives, the number ICMP negotiates
  • firewall — the place the bad "block all ICMP" rule usually hides
  • ping — Echo Request/Reply, the heartbeat tool
  • traceroute — Time Exceeded turned into a path map
  • tcpdump — watch ICMP on the wire, see the MTU message arrive (or not)
  • iptables — where you allow Fragmentation-Needed and rate-limit Echo
  • network failure — when the back-channel goes quiet, this is the playbook

Are large transfers hanging while small requests sail through?

CleverUptime watches your endpoints from the outside and your server from the inside, so when a download stalls or a NIC starts dropping packets, it flags the symptom in plain language instead of leaving you to suspect everything but the firewall rule that's eating your Packet-Too-Big messages.

Want to see your own server's health right now? One command, no signup, no install.

Check your server →