1

I wonder if there is a way to bypass kernel and redirect the ping packet to my network gateway when I ping my self IP address.

For example, my private IP address is 172.31.42.99 and the gateway IP is 172.31.32.1. What I want to do is when I do "ping 172.31.42.99" I will get it the result of "ping 172.31.32.1".

I am not an expert of IP Routing table or iptables, I am not sure if there is a way by editing routing table or iptables to achieve this?

I tried to edit routing tables using sudo ip route add 172.31.42.99/32 via 172.31.32.1 and also tried to edit iptables PREROUTING chains in nat table. Neither of them worked.

I do appreciate any help or ideas.

chengzx
  • 23
  • 3
  • 2
    You could do some weird staff with proxy ARP, but the question is, why? What are you trying to do? If you explain that, somebody might propose a saner solution. – Eduardo Trápani Apr 10 '20 at 22:59
  • Without @A B's router work around this is hard to achieve. It is akin to trying to receive a packet on an interface that is in the process of sending it. Some routers would just drop the badly routed packet rather that attempting to return it. – Rowan Hawkins Apr 11 '20 at 07:55
  • I'm rereading the exact question I'm not sure I actually answered exactly to this question. I'll add an alternate (and simpler) answer in my answer – A.B Apr 11 '20 at 09:46

1 Answers1

2

UPDATE: giving here an answer more faithful to

when I do "ping 172.31.42.99" I will get it the result of "ping 172.31.32.1".

and not involving any special routing or needing 172.31.32.1 to be a router: simple DNAT.

using netfilter OUTPUT hooks

The linux networking stack has netfilter hooks inserted between different routing steps as can be seen in this schematic. It has to be noted that incoming packets are not treated at the same place than outgoing locally originated packets. While netfilter hooks for the former are in PREROUTING hooks, the hooks for the latter are in OUTPUT hooks: that's where a nat rule should be put, not in the nat/PREROUTING which wouldn't see this traffic. Since I don't know what else exists, just in case, I insert it before any other rule:

iptables -t nat -I OUTPUT -p icmp -d 172.31.42.99 -j DNAT --to-destination 172.31.32.1

Actions done in OUTPUT hooks are eligible to trigger a reroute check (as seen in the previous schematic): alter the originally chosen route for the given packet. Note that this doesn't alter other characteristics of the IP packet: for example the source IP is not changed. For OP's specific case it's fine, the source will already be correct. For many other cases, often other awkwards changes must also be made (like SNAT in nat/POSTROUTING).

# ping 172.31.42.99
PING 172.31.42.99 (172.31.42.99) 56(84) bytes of data.
64 bytes from 172.31.42.99: icmp_seq=1 ttl=64 time=0.090 ms
64 bytes from 172.31.42.99: icmp_seq=2 ttl=64 time=0.122 ms
64 bytes from 172.31.42.99: icmp_seq=3 ttl=64 time=0.105 ms
^C
--- 172.31.42.99 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 89ms
rtt min/avg/max/mdev = 0.090/0.105/0.122/0.017 ms

at the same time:

# conntrack -E -p icmp
    [NEW] icmp     1 30 src=172.31.42.99 dst=172.31.42.99 type=8 code=0 id=2029 [UNREPLIED] src=172.31.32.1 dst=172.31.42.99 type=0 code=0 id=2029
 [UPDATE] icmp     1 30 src=172.31.42.99 dst=172.31.42.99 type=8 code=0 id=2029 src=172.31.32.1 dst=172.31.42.99 type=0 code=0 id=2029

(some time later)

[DESTROY] icmp     1 src=172.31.42.99 dst=172.31.42.99 type=8 code=0 id=2029 src=172.31.32.1 dst=172.31.42.99 type=0 code=0 id=2029

The equivalent using nftables instead of iptables could have been:

# nft add table ip mynat
# nft add chain ip mynat dnaticmp '{ type nat hook output priority -100; policy accept; }'
# nft add rule ip mynat dnaticmp ip protocol icmp ip daddr 172.31.42.99 dnat to 172.31.32.1

Previous answer based on doing tricks with routing tables

... which was probably not what OP intended

I'm giving you here enough rope to hang yourself.

Prerequisite (which is fullfilled by OP): 172.31.32.1 must be a router (and its intended behaviour has only be tested below with a Linux router, so it might also have to be a Linux router in case behaviour varies).

I will assume (not specified in OP) that the IP network is 172.31.32.0/20 and the system's network interface is called eth0.

The reason you didn't manage to change results by altering visible routes is that when adding an address to an interface, some routes are also automatically added in the ... hidden local routing table. This table has the lowest precedence priority because it's called from rule priority 0:

# ip rule show
0:      from all lookup local
32766:  from all lookup main
32767:  from all lookup default

# ip route show table local
broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1 
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1 
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1 
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1 
broadcast 172.31.32.0 dev eth0 proto kernel scope link src 172.31.42.99 
local 172.31.42.99 dev eth0 proto kernel scope host src 172.31.42.99 
broadcast 172.31.47.255 dev eth0 proto kernel scope link src 172.31.42.99 

# ip route get from 172.31.42.99 172.31.42.99
local 172.31.42.99 from 172.31.42.99 dev lo uid 0 
    cache <local> 

Here the goal is to override for the local case the previous entry. The only way is to "move" the rule 0 to a higher precedence value to be able to insert something before:

# ip rule add pref 100 from all lookup local
# ip rule delete pref 0

And add a rule that will override routes to a newer routing table, but only for the locally originated output case, which translates to the special syntax iif lo really meaning outgoing rather than from interface lo:

# ip rule add pref 50 iif lo lookup 500

and the needed route (which will take care of ARP resolution by setting the router's IP as gateway):

# ip route add table 500 172.31.42.99/32 via 172.31.32.1

We now have two valid routes (so each direction will even pass Strict Reverse Path Forwarding when swapping interfaces: eth0 and lo).

  • the special route added:

      # ip route get from 172.31.42.99 172.31.42.99
      172.31.42.99 from 172.31.42.99 via 172.31.32.1 dev eth0 table 500 uid 0 
          cache 
    
  • and also, only since now become valid in the other direction:

      # ip route get from 172.31.42.99 iif eth0 172.31.42.99
      local 172.31.42.99 from 172.31.42.99 dev lo table local 
          cache <local> iif eth0 
    

Result:

# ping 172.31.42.99
PING 172.31.42.99 (172.31.42.99) 56(84) bytes of data.
From 172.31.32.1: icmp_seq=1 Redirect Host(New nexthop: 172.31.42.99)
64 bytes from 172.31.42.99: icmp_seq=1 ttl=63 time=0.147 ms
From 172.31.32.1: icmp_seq=2 Redirect Host(New nexthop: 172.31.42.99)
64 bytes from 172.31.42.99: icmp_seq=2 ttl=63 time=0.129 ms
From 172.31.32.1: icmp_seq=3 Redirect Host(New nexthop: 172.31.42.99)
[...]

This is only working because 172.31.32.1 is a router. What actually happens for a single ping is:

  • system sends the ICMP request to the configured gateway: the router,
  • router detects the system is misconfigured and there's a better direct destination in the same LAN (which happens to be the system itself) to handle this request and sends an ICMP redirect to tell it to correct its behaviour. A non-routing node would simply have dropped the packet,
  • router still routes the ICMP echo request to the destination 172.31.42.99: back to the sender,
  • system receives an ICMP echo (with a valid route and even passing the Strict Reverse Path Forwarding rules) and sends back an ICMP reply the same way as before,
  • router sends again an ICMP redirect,
  • router still routes the ICMP reply,
  • system (and command ping) receives the ICMP reply.
A.B
  • 11,090
  • 2
  • 24
  • 45
  • This is more idle curiosity, but I wonder if the latency on the router is enough to redirect the packet if the connection were half-duplex. Not many people have half-duplex links any more. – Rowan Hawkins Apr 11 '20 at 08:01
  • @RowanHawkins Do you mean that the reply could come too early and there would be collisions? I would think that beside changing the delay because of retransmissions from layer 1(?) this would be invisible to the layers above anyway. And I wouldn't even know how to simulate such an half-duplex link (I'm usually using network namespaces for simulation). – A.B Apr 11 '20 at 09:26
  • Solved my issue, it turned out I need to change the OUTPUT chain of nat table, thanks a lot! – chengzx Apr 15 '20 at 20:51