Iptables – multi-hop routing without NAT


i recently started to play a little bit with iptables and routing and came across the following problem:

i have four servers A,B,C,D with ip_forwarding enabled and my goal is simply to route icmp echo requests through the chain while keeping the original IP. unfortunately, the forwarding is not working properly.

(A = = eth0) <—> (B = = eth0, = eth1) <—> (B = = eth0, = eth1) <—> (D= = eth0)

further, I have the following routes:

  • A: via
  • B: via
  • C: –
  • D: –

so my intention behind this toy example was that requests are routed through the chain according to the static routes. Of course i know that the ping won't succeed, cause D & C do not have routes to A. But my intention was simply to detect the traffic with tcpdump.

In general, i thought that the packets would simply be forwarded by looking up routes in the routing table and are then sent to the respective destination or gateway. However, without using NAT this does not seem to work. the icmp requests are received by C on eth0, however, C does not forward them to D.

Of course I could use some masquerade/snat on B and (i haven't tested it), but i'm quite sure that D will receive the request from B ("instead" of A).

Is this in general possible to perform "multi-hop" routing without using NAT?
Or are there only some iptables FORWARD rules missing which would allow me to do this?

Is there any possibility to debug/lookup what iptables does with the packets? currently, i only found the LOG target, but to be honest, create all rules twice is really annoying..

Best Answer

Linux implements RFC1704's reverse path forwarding/filtering, and most distributions enable it by default. That is, allow receiving/forwarding packets only if the reverse path (as defined by the route table) would use the same outgoing interface as the interface the packet came in (or with loose mode, if there is any interface which would have a route for this packet). It's done to protect against spoofing.

To do tests where asymmetric routes or when other unconventional settings are done, just disable rp_filter on both involved interfaces (+"all") on each node (here C and D are enough, others have sufficient routes):

for i in /proc/sys/net/ipv4/conf/{eth[01],all}/rp_filter; do echo 0 > "$i"; done

Doing this allows server D to receive the icmp ping (but of course it can't reply).

Related Topic