1

I am trying to change the destination IP address for an ICMP reply packet.

The ICMP reply enters the router from my IPSEC tunnel as such (I'm not entirely certain why it is shown in tcpdump twice):

14:28:09.562030 IP 35.182.188.86 > 54.76.131.136: ICMP echo request, id 28997, seq 1259, length 64
14:28:09.641595 IP 54.76.131.136 > 35.182.188.86: ICMP echo reply, id 28997, seq 1259, length 64
14:28:09.641645 IP 54.76.131.136 > 35.182.188.86: ICMP echo reply, id 28997, seq 1259, length 64

I attempt to change the destination ip to local ip (172.31.20.219) on the PREROUTING table with:

sudo iptables -t nat -A PREROUTING --source 54.76.131.136 --destination 35.182.188.86 -j DNAT --to-destination 172.31.20.219

So the tables looks like this:

ubuntu@ip-172-31-23-13:~$ sudo iptables-save -c
# Generated by iptables-save v1.6.0 on Tue Oct  3 14:58:19 2017
*nat
:PREROUTING ACCEPT [33:2711]
:INPUT ACCEPT [32:2627]
:OUTPUT ACCEPT [8:1080]
:POSTROUTING ACCEPT [9:1164]
[0:0] -A PREROUTING -s 54.76.131.136/32 -d 35.182.188.86/32 -j DNAT --to-destination 172.31.20.219
COMMIT
# Completed on Tue Oct  3 14:58:19 2017
# Generated by iptables-save v1.6.0 on Tue Oct  3 14:58:19 2017
*raw
:PREROUTING ACCEPT [2646:283498]
:OUTPUT ACCEPT [998:168846]
[1954:164136] -A PREROUTING -s 54.76.131.136/32 -d 35.182.188.86/32 -j LOG
[821:68964] -A PREROUTING -s 54.76.131.136/32 -d 35.182.188.86/32 -j TRACE
COMMIT
# Completed on Tue Oct  3 14:58:19 2017

However, the ip change does does not take place.

Using

sudo iptables -t raw -A PREROUTING --source 54.76.131.136 --destination 35.182.188.86 -j LOG

I can see that the match can be made on the raw table:

Oct  3 14:25:20 ip-172-31-23-13 kernel: [  889.588090] IN=eth0 OUT= MAC=02:fc:a0:12:56:64:02:7f:fe:dc:a4:0d:08:00 SRC=54.76.131.135 DST=35.182.188.85 LEN=84 TOS=0x00 PREC=0x00 TTL=63 ID=64700 PROTO=ICMP TYPE=0 CODE=0 ID=28997 SEQ=1091

But it doesn't seem to make a match on the nat table.

Can anyone offer advice on why the destination ip isn't changed or how to debug this further? Seems very weird.

I am using Ubuntu 16.04. The incoming traffic is coming in on an IPSEC tunnel setup with StrongSwan.

Thanks

1 Answers1

1

An ICMP Echo reply packet is part of an established flow. That means, when the kernel received such a packet in a normal case, it was already expecting it (after the initial ICMP Echo request) , so conntrack, the base brick needed for nat, already created an expectation entry for it. In such case, the nat table will be short-circuited and not even executed for this reply packet.

reference: https://www.netfilter.org/documentation/HOWTO/netfilter-hacking-HOWTO-3.html#ss3.2 (NAT)

This table is slightly different from the `filter' table, in that only the first packet of a new connection will traverse the table: the result of this traversal is then applied to all future packets in the same connection.

To check this behavior, use the conntrack command (from the conntrack package) in event monitor mode. Here's an example with one ping to Google:

# conntrack -E
    [NEW] icmp     1 30 src=192.168.3.2 dst=8.8.8.8 type=8 code=0 id=12837 [UNREPLIED] src=8.8.8.8 dst=198.51.100.5 type=0 code=0 id=12837
 [UPDATE] icmp     1 30 src=192.168.3.2 dst=8.8.8.8 type=8 code=0 id=12837 src=8.8.8.8 dst=198.51.100.5 type=0 code=0 id=12837
[DESTROY] icmp     1 src=192.168.3.2 dst=8.8.8.8 type=8 code=0 id=12837 src=8.8.8.8 dst=198.51.100.5 type=0 code=0 id=12837

The first packet will create the [NEW] entry and so will pass the nat table giving a chance to alter the expectation (in this example a classical SNAT/MASQUERADE). Other packets of this flow are handled only with the conntrack expectation until the entry is destroyed (30s timeout for this icmp).

Using a nat rule would have worked with the initial ICMP Ping Echo request, because it would have been a new flow.

You didn't tell what you actually want to do with all of this. If your goal is to copy the packet to 172.31.20.219, you should try the TEE target (man iptables-extensions).

A.B
  • 11,090
  • 2
  • 24
  • 45
  • Great! I had suspected something like this, as you say the request is NAT'd fine it's only the reply that doesn't seem to be NAT'd. My goal is to try and route traffic from a public ip within my private network through the IPSEC tunnel to another private network where the endpoint host is also public IP. I am attempting to NAT the private IP of the host to the public IP before sending it across the tunnel (and vice versa). – noflowcontrol Oct 04 '17 at 09:14
  • So you want to route those packets? Can't you, at least to have this example work, put a few static routes on each node, telling that the other node is the route for the other public IP and not use NAT at all? AFAIK that's not a problem to route public IPs between private IP nodes. That would just disrupt things like traceroute. – A.B Oct 04 '17 at 18:39