Are DNAT and REDIRECT equivalent when applied to locally destined traffic?

In setting up our OpenStack environment, I ran into a problem that was preventing instances from contacting a server running on the host. The metadata service (which exposes an HTTP API) runs on port 8775 on the host, and the OpenStack networking code adds the following DNAT rule to grant access via a special address on port 80:

-A PREROUTING -d 169.254.169.254/32 
  -p tcp -m tcp --dport 80 -j DNAT --to-destination 127.0.0.1:8775 

Instances are connected to the host via a local bridge device, and 169.254.169.254 is assigned to lo.

While this rule successfully matches packets originating from a guest instance trying to access http://169.254.168.254/, they never reach the listening service.

Replacing this DNAT rule with a largely equivalent REDIRECT makes everything work correctly:

-A PREROUTING -d 169.254.169.254/32 
  -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8775 

I'm trying to understand why the REDIRECT works and the DNAT fails. It's not clear from the iptables documentation whether or not the DNAT rule is expected to work for locally destined traffic. I'm hoping someone here can provide an authoritative answer, ideally backed up by documentation, or can suggest what might be amiss in my configuration that is preventing the DNAT rule from working as expected.


Solution 1:

What you're trying to do is explicitly denied by the kernel.

You can't send packets from non 127/8 addresses to 127/8. The kernel filters them out and drops them as it considers them "martians".

This presumably happens because rp_filter defaults to on. Possibly disabling it might alter this behavior (although that will disable some potentially crucial security protection).

With the REDIRECT you're not changing the IP, which is why it works. To put it another way, if you're going to use DNAT you can send it to anything outside of 127.0.0.0/8.

Solution 2:

According to the netfilter NAT HOWTO:

There is a specialized case of Destination NAT called redirection: it is a simple convenience which is exactly equivalent to doing DNAT to the address of the incoming interface.

It's slightly more intelligent in that it chooses the DNAT target address as the address of the device which the packet was received on. In your case it would DNAT the packets to the address on the bridge device, and not to 127.0.0.1.