I have a Linux virtual server with 2 NICs.
eth0 <IP1>
eth1 <IP2>
arp_filter
is turned on and rp_filter
is set to be 2(loose mode).
Policy-routing is configured as the following:
table T1
default via <GW> dev eth0 src <IP1>
127.0.0.0/8 dev lo
<LAN> dev eth0 src <IP1>
table T2
default via <GW> dev eth1 src <IP2>
127.0.0.0/8 dev lo
<LAN> dev eth1 src <IP2>
ip rule add from <IP1> table T1
ip rule add from <IP2> table T2
After that, I can ping
both binding floatingips of <IP1>
and <IP2>
from outside. However ping -I eth1 <some_domain>
dosen't work. tcpdump
shows that when I ping from eth1
to outside, Linux directly asks MAC
of the outside address, which is incorrect because they are not in the same LAN
.
Here is tcpdump
data:
root@rm-2:~# tcpdump -i eth1 arp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
17:53:08.696191 ARP, Request who-has 172.30.250.119 tell 172.30.248.2, length 46
17:53:08.728482 ARP, Request who-has 172.30.251.144 tell 172.30.251.138, length 46
17:53:09.447252 ARP, Request who-has 61.135.169.125 tell 172.30.251.43, length 28
17:53:09.551514 ARP, Request who-has 172.30.250.127 tell 172.30.248.2, length 46
17:53:09.698076 ARP, Request who-has 172.30.250.119 tell 172.30.248.2, length 46
17:53:09.859046 ARP, Request who-has 172.30.248.246 tell 172.30.248.245, length 46
17:53:10.446009 ARP, Request who-has 61.135.169.125 tell 172.30.251.43, length 28
17:53:10.477104 ARP, Request who-has 172.30.250.128 tell 172.30.248.2, length 46
As you can see, 61.135.169.125
is a foreign address, is this a bug or something?
EDIT
Output of route
: // 172.30.248.1 is the GW
Destination Gateway Genmask Flags Metric Ref Use Iface
default 172.30.248.1 0.0.0.0 UG 0 0 0 eth0
Answer:
You need to add an output interface rule (
ip rule add oif ...
) in addition toip rule add from ...
, because ping is binding to the interface, not the IP.Example:
Explanation:
The ping example in your question is using the interface as the source (
ping -I eth1 <some domain>
), which does not have any matching policy routes. So ping is behaving exactly like it would if the there were no routes defined for the interface at all.Example to test/prove (starting without policy routing):
Using my phone USB tethered as an alternate route, I have the following base configuration.
Because the desktop usb0 interface is assigned a /32 address, if I try to
ping 192.168.42.129 -I 192.168.42.1
, it will fail because there is no route defined for that address, and it is not within the broadcast domain of the usb0 address. However, withping 192.168.42.129 -I usb0
-- I am telling ping to use the interface itself, and there are no routes matching the interface (thus, no concept of a broadcast domain), so it will blindly trigger an ARP request for any IP that is not it's own.Lets attempt to ping using the interface (no routes). This will cause an ARP request to occur even though it is not within the same broadcast domain:
Using the source IP of the interface (no routes), it does not make an ARP request because the source is not within the broadcast domain:
Now if I add a route to the host over the interface, ping knows it can make an ARP request for the 192.168.42.129 address:
So the same concept applies when I try to ping something off-network; if I ping 8.8.8.8 using the interface as the source, it will blindly make an ARP request sans any matching routes:
When using the interface address, the lack of any kind of next-hop route in the routing table will cause it to fail and not make an ARP request:
So lets add policy routing for the 192.168.42.1 address (using "ip rule from ...") to use 192.168.42.129 as the next-hop default, in the same manner as your question example:
It works, because we are using the address, it rightfully matches the ip rule.
Now we try the same ping again using the interface:
It fails; there no interface route for the next-hop, so it will again make an ARP request, which will never get replied to. So we need to add an ip rule for the interface to use 192.168.42.129 as the next-hop as well:
I believe generally, the lack of an interface route would not have had a negative impact on your implementation for normal, non-interface bound, outgoing connections. Most (not all) applications bind to the address for outbound TCP/UDP connections, and only bind to the interface for incoming connections (listening). The ping utility is a special case.
To prove this, if I remove the interface rule from the routing policy, I am still able to use normal outbound sockets when specifying the bind address. In the example below I use telnet and netcat, in both cases specifying the bind address (
-b 192.168.42.1
) and it properly matches the T1 table, and thus uses the gateway.I ran into this same issue while testing a policy-route implementation, and it took me a bit wrap my head around why my interface pings were unanswered. Hopefully this clears it up.