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).
- 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
- Reply comes back, finds a flow table entry, gets translated from
(src: b, dst: a) to (src: B, dst: a)
- The link between R2 and E2 comes down
- E1 sends another packet towards E2: Gateway translates
(src: a, dst: B) to (src: a, dst: b)
- Router R2 sends an ICMP Error (Destination Unreachable) to E1
- Gateway NATs the ICMP Error message
- Outer packet: ICMP
(src: r2, dst: a)
- Inner embedded packet:
(src: a, dst: b)
- Gateway finds this inner header, looks up for reversed flow
(src: b, dst: a), finds it
- Gateway applies the reverse transformation associated to the flow, to the inner packet header, turning it into
(src: a, dst: B), that's OK
- 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).
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:
E1 reaches out to E2 using port forwarding on E2's end, so
(src: a, dst: B)becomes(src: a, dst: b).(src: a, dst: B): change dst tob(src: b, dst: a): change src toB(src: b, dst: a)to(src: B, dst: a)(src: a, dst: B)to(src: a, dst: b)(src: r2, dst: a)(src: a, dst: b)(src: b, dst: a), finds it(src: a, dst: B), that's OK(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
Bor fromr2?Discussion:
r2(hence we should useB).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:
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: