Skip to content

NAT + ICMP Error messages: Should we overwrite the source IP? #1579

@qmonnet

Description

@qmonnet

TL;DR

When NAT-ing ICMP Error messages with port forwarding or masquerading, we may overwrite the IP address of the sender of the Error message in the outer IP header with the IP address for the unavailable destination.

More details

Port forwarding

Consider the following configuration:

Endpoint E1   <--->   Router R1   <--->   Gateway   <--->   Router R2   <--->   Endpoint E2
IP:port = a               r1                                    r2              IP:port = b
                                                                       (port-forwarded as B)

E1 reaches out to E2 using port forwarding on E2's end, so (src: a, dst: B) becomes (src: a, dst: b).

  1. First packet goes through from E1 to E2, the gateway creates the following flows:
    • (src: a, dst: B): change dst to b
    • (src: b, dst: a): change src to B
  2. Reply comes back, finds a flow table entry, gets translated from (src: b, dst: a) to (src: B, dst: a)
  3. The link between R2 and E2 comes down
  4. E1 sends another packet towards E2: Gateway translates (src: a, dst: B) to (src: a, dst: b)
  5. Router R2 sends an ICMP Error (Destination Unreachable) to E1
  6. Gateway NATs the ICMP Error message
    1. Outer packet: ICMP (src: r2, dst: a)
    2. Inner embedded packet: (src: a, dst: b)
    3. Gateway finds this inner header, looks up for reversed flow (src: b, dst: a), finds it
    4. Gateway applies the reverse transformation associated to the flow, to the inner packet header, turning it into (src: a, dst: B), that's OK
    5. Gateway also applies the transformation associated to the flow, to the outer header: the outer IP header becomes (src: B, dst: a)

The question is the following: at step 6.v), should we overwrite the router's IP address with the one for the initial destination for the packet? Should the ICMP Error message that E1 sees be from B or from r2?

Discussion:

  • One argument is that the packet never reached E2, so it's weird to see the ICMP Error message coming back from E2.
  • One counter-argument is that we should not “leak” R2's IP to E1, so it shouldn't be r2 (hence we should use B).

To be fair, it probably doesn't matter much to the client on E1: it gets an ICMP Error message, and what matters to it is the inner packet that tells what request went wrong, and we process the inner packet correctly. But we might want to think more about this and adjust the outer packet's sender IP too, at some point.

Masquerading

Note that something similar happens with masquerading (instead of port forwarding), except in a different direction:

Endpoint E1   <--->   Router R1   <--->   Gateway   <--->   Router R2   <--->   Endpoint E2
IP:port = a               r1                                    r2              IP:port = b
(masqueraded as A)

In this situation, E1 reaches out to E2, then E2 attempts to reply but the link between E1 and R1 is gone - this will trigger an ICMP Error message from R1 (outer header: (src: r1, dst: b)) and the Gateway will also translate the source IP (outer header becomes (src: A, dst: b)).

Static NAT

Static NAT behaves differently: no flow table lookup, and no unconditional overwriting of the source address. There are two different cases:

  • The source IP for the ICMP Error message from the router can be translated (there is a valid mapping, as per the Expose blocks defined), and in that case we translate it - makes sense, we translate the address of the router into whatever the initial endpoint would use to reach out to it
  • The source IP for the ICMP Error message from the router cannot be translated, in which case it is left unchanged (contrarily to stateful NAT, we don't overwrite with the endpoint's address - so there's also a consistency issue, here).

Metadata

Metadata

Assignees

Labels

area/natRelated to Network Address Translation (NAT)questionFurther information is requested

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions