Networking · IP and Routing

ICMP Errors as the Only Feedback Channel for Forwarding

Which errors a router is allowed to generate, what it must quote back, and why rate limiting and filtering turn a diagnostic protocol into a silent failure mode.

IP is a datagram protocol with no acknowledgement of its own. When a router cannot forward a packet, nothing in the packet’s own format can carry that information back: there is no field for “this failed”, and the sender’s address is available but the sender has no reason to be listening for a reply. The error messages defined in RFC 792 exist to fill that gap, and they are the only mechanism by which the forwarding plane tells a sender that its assumption about the path was wrong.

The errors a forwarder may generate

Four conditions account for almost every ICMP error a router produces. The first is that no route matches: the forwarding table has no entry covering the destination, so the packet is discarded and ICMP Destination Unreachable with code 0, network unreachable, is returned. The second is that a route exists but points at a next hop that cannot be reached, which produces code 1, host unreachable, or on a link where resolution fails, code 1 as well.

The third is the hop limit. A router that receives a packet with a time-to-live of one, or that decrements it to zero, discards it and returns Time Exceeded with code 0. This is not an error in any useful sense; it is the mechanism by which loops are bounded and paths are traced. The fourth is a size problem: a packet larger than the outgoing MTU with the don’t-fragment bit set produces Destination Unreachable code 4, carrying the MTU, as described in fragmentation and path MTU discovery.

A fifth case is administrative: a filter that discards a packet is expected to return code 13, communication administratively prohibited, rather than dropping silently. Whether it does is a policy decision, and the decision has consequences. A silent drop is indistinguishable from a routing failure, so the sender learns nothing and the operator who wrote the filter sees no trace of the packet.

Each of these messages is generated by the router that discarded the packet, addressed to the packet’s source, and sent through the same forwarding process as any other packet. That last detail is easy to miss and it produces a real failure: if the route back to the source is broken, the error cannot be delivered, and the sender sees silence. The router cannot report the problem with the path in one direction using a message that must travel the other direction. The lookup that delivers the error is an ordinary one, subject to the same longest-prefix decision as longest-prefix match and forwarding structures, which is why an error can itself be dropped by a route that was installed for a different purpose.

What must be quoted, and why eight bytes

An ICMP error carries the original packet’s IP header and the first sixty-four bits of its payload. The header is needed so the sender can identify which datagram failed; the eight payload bytes are needed so the sender can identify which transport flow it belonged to. For TCP and UDP, the first eight bytes of the transport header contain the source port and destination port, which is enough to match the error to a socket.

Sixty-four bits is exactly enough for the port pair and, in TCP, the sequence number. A sender that wanted to verify the error against the full datagram would need the whole payload, which would make the error message as large as the packet that caused it. The truncation is a deliberate compromise, and it has a consequence for tunnelled traffic: if the outer header is quoted, the sender learns about the outer flow but not the inner one, so an error generated inside a tunnel may never reach the application that cares.

The quoted header also makes ICMP errors stateful in a way that matters for translation. A device that rewrites addresses must rewrite the quoted copy to match, or the sender will fail to match the error to any socket it owns. This is a frequent cause of discovery failures at NAT boundaries, because the message arrives, is correctly formed, and is discarded by the transport layer as not belonging to any connection.

The rule against erroring about errors

An ICMP error is never generated in response to an ICMP error. The quoted packet inside an error message is never itself the cause of another error. The rule prevents infinite recursion: two routers that cannot reach each other would otherwise generate errors about each other’s errors indefinitely.

The same principle extends to the other cases RFC 1812 lists. An error is not generated for a packet addressed to a broadcast or multicast destination, nor for one whose source address is invalid or carries a network prefix of zero, because in each case there is no unambiguous party to notify. It is not generated for a fragment other than the first, because the quoted header would not identify the transport flow, nor for a packet that fails header validation, because the source address cannot be trusted. These exceptions are not arbitrary; each removes a case where the error would either loop or be useless.

Multicast forwarding has a related rule. A router that cannot forward a multicast packet does not return an error to the source, because the source is one host and the receivers may be many, and the router has no way to know which of them are affected. Failures in multicast are reported by the routing protocol rather than by ICMP, which is a different mechanism operating on a different timescale.

Rate limiting and its side effects

A router that generates an error for every discarded packet becomes a packet amplifier pointed at the source address, which may be forged. An attacker sending packets with a spoofed source causes the router to send the errors to the victim, multiplying the traffic. RFC 1812 requires that a router be able to limit the rate at which it sends Source Quench, and recommends the same ability for the other error types, leaving the granularity, per router or per interface, to the implementation. A token bucket is the usual way to express the limit, because it bounds the sustained rate and the burst separately, so a sender cannot rely on any particular error being delivered.

The mechanism is therefore lossy by design. A sender probing for the path MTU may see some of its probes rejected with an MTU message and others dropped without one, and the discovery loop must tolerate that. A sender that treats each lost probe as evidence of a different problem will misdiagnose. Where the limit is kept per destination, a sender with a persistent problem sees only a fraction of the errors its packets generate, while a single bad packet to a destination with its own allowance is more likely to draw a reply.

/* Token-bucket shape for error generation, per destination. */
struct icmp_bucket {
    uint64_t tokens;          /* in units of 1/1000 token */
    uint64_t last_refill;     /* nanoseconds */
};

static bool may_send_error(struct icmp_bucket *b, uint64_t now)
{
    const uint64_t per_ms = 1000;          /* tokens are in 1/1000 units */
    const uint64_t capacity = 4000;        /* at most four tokens saved up */

    b->tokens += (now - b->last_refill) / 1000000 * per_ms;
    if (b->tokens > capacity)
        b->tokens = capacity;
    b->last_refill = now;

    if (b->tokens < 1000)                  /* less than one whole token */
        return false;
    b->tokens -= 1000;
    return true;
}

The second side effect is filtering. Because ICMP has been used for reconnaissance and for amplification, many networks block it wholesale at their borders. The blocking is indiscriminate: it removes echo requests, which is a minor loss, and it removes the error messages that discovery and congestion signalling depend on, which is not. The result is that a path can be fully functional for small packets and completely broken for large ones, with no signal at either end.

Traceroute as an exploitation of the hop limit rule

The hop limit error is the foundation of path tracing. A probe is sent with a time-to-live of one; the first router decrements it to zero, discards the packet, and returns Time Exceeded quoting the probe. The sender reads the router’s address from the error’s source field and learns the first hop. Repeating with increasing values walks the path outward until the probe reaches the destination, which answers with a different message or with a transport-layer response.

Three details of the mechanism are visible in the output of any trace. The probe must use a port or protocol the destination will reject or ignore in a way that produces a distinguishable reply, which is why implementations vary between UDP ports, ICMP echo, and TCP segments. The error may be rate limited, so a hop that does not answer may simply have suppressed the message rather than being absent. And the return path of the error may differ from the forward path of the probe, so the address reported is the address of the router on the interface closest to the sender, not necessarily the interface the probe traversed.

That last point is a genuine ambiguity rather than an implementation defect. A router with several interfaces has several addresses, and the error is sourced from whichever the stack selects for the route back. The forward path and the reverse path are chosen independently, so the trace describes a sequence of routers that may not be the sequence the probe actually followed.

Where ICMP stops being useful

The protocol reports failures of individual packets, not failures of paths. A route that flaps between two next hops produces errors in bursts that are indistinguishable from a persistent failure. A path that is merely slow produces no errors at all, because slowness is not an error condition in IP; it is visible only through the transport layer’s timing. A path that silently drops a fraction of traffic, which is common under congestion, produces errors only if the drop is the result of a policy that chooses to report it.

The consequence is that ICMP is necessary but insufficient for diagnosis. It is the only in-band signal the forwarding plane has, and it is deliberately rate limited, selectively filtered, and truncated. Every mechanism that depends on it inherits those properties, which is why the design of discovery protocols assumes the message may never arrive and includes a fallback, and why a network that filters ICMP must be diagnosed with out-of-band tools rather than from the endpoints.

IPv6 changes the message set and keeps the structure. RFC 4443 defines a smaller set of types with the same quoting rule and the same prohibition on erroring about errors, and adds Packet Too Big as a mandatory message rather than an optional one. The reliance on delivery is stronger there, because IPv6 has no router fragmentation to fall back on, so a filtered error message is a hard failure rather than a performance problem.

References

  1. RFC 792: Internet Control Message Protocol — IETF
  2. RFC 1812: Requirements for IP Version 4 Routers — IETF
  3. RFC 4443: Internet Control Message Protocol (ICMPv6) for the Internet Protocol Version 6 (IPv6) Specification — IETF