skip to content

When an ICMPv4 error reaches a host, how does its stack find the TCP connection or UDP socket concerned, and why check further?

level: seniorimportance: should knowfreq 25%

answer

  1. the outer source is just the reporter
  2. read the quote, not the envelope
  3. quoted source is you
  4. protocol field picks the transport
  5. sequence number inside the flight

basics

~20 s

The host ignores the outer header and reads the quote: its Protocol field picks TCP or UDP, and its addresses and ports, the host's own as source, name the flow. Forged errors are easy, so TCP also checks the quoted sequence number.

solid answer

~50 s

The outer IPv4 source of an ICMP error is just whoever reported it, often a router, so it identifies nothing. The host reads the quoted datagram instead: RFC 1122 requires it to take the quoted `Protocol` field and hand the error to that transport. The quoted packet is one the host itself sent, so its quoted **source** address and port are local and its **destination** is the peer; that four-tuple names the TCP connection, and the local port names the UDP socket. Then it decides whether to believe it. Anyone who can guess a four-tuple can forge an error, so many TCP implementations also require the quoted sequence number to lie between `SND.UNA` and `SND.NXT`, data sent but not yet acknowledged, as RFC 5927 describes. RFC 1122 also limits the reaction: net, host and source-route-failed unreachables are soft errors that must not abort a connection.

code

pseudocode · 15 lines
pseudocode
on_icmp_error(outer_ip, icmp):
  if not checksum_ok(icmp) or icmp.type not in KNOWN_ERROR_TYPES: drop
  q = icmp.quoted_ip_header
  if len(icmp.data) < q.ihl * 4 + 8: drop
  ports = first_8_bytes_after(q)
  # the quoted packet is one WE sent: its source is local
  if q.protocol == TCP:
    conn = tcp_lookup(q.src, ports.src, q.dst, ports.dst)
    if conn is none: drop
    if not (conn.snd_una <= ports.seq < conn.snd_nxt): drop
    conn.report(icmp.type, icmp.code)
  elif q.protocol == UDP:
    sock = udp_lookup(q.src, ports.src)
    if sock is none: drop
    sock.report(icmp.type, icmp.code)

go deeper

for a junior

Recall that the error's quoted packet, not its outer header, tells the host which connection had the problem, because it is a copy of what the host sent.

for a middle

Walk through the demultiplexing: quoted Protocol field to pick TCP or UDP, then quoted addresses and ports read as outbound, with the local side as source.

for a senior

Explain why a matched error is still untrusted, how the sequence-window check limits forged errors, which unreachable codes RFC 1122 treats as soft, and what a NAT must rewrite.

for a principal

Weigh how much to trust unauthenticated ICMP: acting on it keeps paths working, ignoring it breaks them, and every middle position is a plausibility check with known gaps.

## The problem the quote solves An **ICMP error** arrives as a fresh IPv4 datagram addressed to a host, but it describes a **different** datagram, one that host sent earlier. The outer header cannot say which conversation is affected: - The outer **source** is whoever generated the error. For a port unreachable that is the destination host; for a net unreachable or a TTL expiry it is some router on the path, an address the host has never talked to. - The outer **destination** is the host itself, which is no help either. - ICMP has **no ports** of its own. Everything needed to identify the flow is in the **quoted datagram**: the original IPv4 header plus at least its first 8 payload bytes (RFC 792, RFC 1122). ## The matching steps 1. **Validate the message.** Check the ICMP checksum, confirm the type is a known error, and confirm the data is long enough to hold the quoted header (its IHL times 4 bytes) plus 8 bytes. RFC 1122 requires unknown types to be silently discarded. 2. **Select the transport.** RFC 1122 section 3.2.2: "the IP protocol number MUST be extracted from the original header and used to select the appropriate transport protocol entity". 3. **Read the quote from the right side.** The quoted packet was **outbound**, so the quoted source address and port are **local**, and the quoted destination address and port are the **peer's**. Reading them as if they were an incoming packet swaps the roles and finds nothing. 4. **Look up the flow.** TCP looks up the full four-tuple of a connection; UDP finds the socket bound to the local address and port, and RFC 1122 section 4.1.3.3 says UDP MUST pass the error to the application. 5. **Decide whether to believe it**, then act according to type and code. ## Why a match is not proof Nothing authenticates an ICMP error. Anyone who can guess or observe the four-tuple can forge one, and RFC 5927 (Informational) catalogues what that buys an attacker against TCP: resetting connections, or shrinking their throughput with forged fragmentation-needed messages. The defences live in how the quote is checked: | Check | What it rejects | |---|---| | Four-tuple lookup | Errors about connections that do not exist | | Quoted sequence number in `SND.UNA` to `SND.NXT` | Errors about data not currently in flight, including stale ones | | Type and code policy | Overreaction, such as aborting on a soft error | RFC 5927 reports that many TCP implementations check the quoted sequence number against data sent but not yet acknowledged. With nothing in flight, no forged error can pass, and with data in flight an attacker who knows the four-tuple must also land inside that window. Stronger authentication does not help here. A host-generated error quotes only 8 bytes of TCP: ports and sequence number. A TCP signature option or an IPsec authentication header covers the whole segment, so it cannot be recomputed from a partial quote. ## What the transport does with it RFC 1122 section 4.2.3.9 tells TCP to act on errors "directing it to the connection that created the error", but not always by aborting: - **Destination Unreachable codes 0, 1 and 5** (net, host, source route failed) are **soft errors**: TCP MUST NOT abort, and SHOULD make the information available to the application. - **Codes 2 to 4** (protocol, port, fragmentation needed) are **hard errors**: TCP SHOULD abort. RFC 5927 reports that most popular implementations nevertheless treat hard errors on synchronized connections as soft, trading slower reaction to genuine errors for robustness against forged ones. - A UDP application is told, but it must cope with asynchronous, possibly delayed errors that belong to an **earlier use of the same port**, a caveat RFC 1122 spells out. ## When a NAT sits in between A host behind a NAT sent its datagram with a private source, but the error was generated after translation, so the quote carries the **public** mapping. RFC 5508 (BCP 148) requires the NAT, on an error arriving from outside, to revert the embedded IP and transport headers using its mapping, leave the type and code unchanged, and set the outer destination to the embedded packet's translated source. Because the ICMP checksum covers the quote, it is recomputed too. A NAT that skips this step leaves the host holding an error it cannot match.

  • Why can't a host match an ICMPv4 error using the error's outer source address?
    Because the outer source is whoever generated the error, not the peer. A port unreachable comes from the destination host, but a net unreachable or a Time Exceeded comes from a router somewhere on the path, an address the host never sent to. The identity of the flow is only in the quoted datagram: its Protocol field, its addresses and its first 8 payload bytes.
  • What must a NAT do to an ICMP error arriving from outside so the inside host can match it?
    RFC 5508 requires it to find the mapping from the embedded packet, revert the embedded IP and transport headers to their private form, leave type and code unchanged, and rewrite the outer destination to the embedded packet's translated source. Checksums change with it, since the ICMP checksum covers the quote. With no active mapping, the NAT should silently drop the error.
  • Why can't a TCP signature option or IPsec authentication protect against forged ICMP errors?
    Because the error quotes only part of the segment. A host-generated ICMPv4 error carries just the IP header and 8 TCP bytes, the ports and sequence number, while a signature covers the whole segment, so the receiver cannot recompute it. RFC 5927 makes this point; the practical defences are plausibility checks such as the sequence-number window.

saying these in an interview costs you the question

  • The host matches an ICMP error by its outer IPv4 source address.
  • In the quoted packet, the source port belongs to the remote peer.
  • An ICMP error matching the four-tuple proves the peer really had a problem.
  • UDP ignores ICMP errors because it is connectionless.
  • Any Destination Unreachable must make TCP abort the connection immediately.