答案:
您需要在ip rule add from ...之外添加一个输出接口规则(ip rule add oif ...),因为ping是绑定到接口,而不是IP。
例子:
ip rule add from <IP1> table T1
ip rule add oif <eth0> table T1
ip rule add from <IP2> table T2
ip rule add oif <eth1> table T2
解释:
您问题中的 ping 示例使用接口作为源 (ping -I eth1 <some domain>),它没有任何匹配的策略路由。因此,如果没有为 interface 定义任何路由,那么 ping 的行为就会完全一样。
测试/证明示例(从没有策略路由开始):
使用我的手机 USB 连接作为备用路由,我有以下基本配置。
Linux desktop:
$ ip addr show usb0
...
inet 192.168.42.1/32 ...
..
Android phone:
$ ip addr show rndis0
...
inet 192.168.42.129/24 ...
...
因为桌面usb0接口分配了/32地址,如果我尝试ping 192.168.42.129 -I 192.168.42.1会失败,因为该地址没有定义路由,并且不在usb0的广播域内地址。但是,使用ping 192.168.42.129 -I usb0 -- 我告诉 ping 使用 interface 本身,并且没有匹配该接口的路由(因此,没有广播域的概念),所以它会盲目触发对任何不属于自己的 IP 的 ARP 请求。
让我们尝试使用接口 ping(无路由)。这将导致 ARP 请求发生,即使它不在同一个广播域内:
desktop$ ping 192.168.42.129 -I usb0
phone# tcpdump -i rndis0 -n icmp or arp
ARP, Request who-has 192.168.42.129 tell 192.168.42.1, length 28
ARP, Reply 192.168.42.129 is-at 3e:04:37:23:05:0e, length 28
IP 192.168.42.1 > 192.168.42.129: ICMP echo request, id 24641, seq 1, length 64
IP 192.168.42.129 > 192.168.42.1: ICMP echo reply, id 24641, seq 1, length 64
使用接口的源IP(无路由),不做ARP请求,因为源不在广播域内:
desktop$ ping 192.168.42.129 -I 192.168.42.1
phone# tcpdump -i rndis0 -n icmp or arp
... nothing comes over the wire, as expected ...
现在,如果我通过接口添加到主机的路由,ping 知道它可以向 192.168.42.129 地址发出 ARP 请求:
desktop$ ip route add 192.168.42.129/32 dev usb0
desktop$ ping 192.168.42.129 -I 192.168.42.1
phone# tcpdump -i rndis0 -n icmp or arp
ARP, Request who-has 192.168.42.129 tell 192.168.42.1, length 28
ARP, Reply 192.168.42.129 is-at 3e:04:37:23:05:0e, length 28
IP 192.168.42.1 > 192.168.42.129: ICMP echo request, id 24667, seq 1, length 64
IP 192.168.42.129 > 192.168.42.1: ICMP echo reply, id 24667, seq 1, length 64
因此,当我尝试在网络外 ping 某些东西时,同样的概念也适用;如果我使用 interface 作为源 ping 8.8.8.8,它将盲目地发出 ARP 请求而没有任何匹配的路由:
desktop$ ping 8.8.8.8 -I usb0
phone# tcpdump -i rndis0 -n icmp or arp
ARP, Request who-has 8.8.8.8 tell 192.168.42.1, length 28
当使用接口地址时,路由表中缺少任何一种下一跳路由都会导致它失败并且不发出ARP请求:
desktop$ ping 8.8.8.8 -I 192.168.42.1
phone# tcpdump -i rndis0 -n icmp or arp
... nothing ...
因此,让我们为 192.168.42.1 地址添加策略路由(使用“来自...的 ip 规则”)以使用 192.168.42.129 作为下一跳默认值,方式与您的问题示例相同:
desktop$ sudo ip rule add from 192.168.42.1 lookup T1
desktop$ sudo ip route add default via 192.168.42.129 dev usb0 table T1
desktop$ ping 8.8.8.8 -I 192.168.42.1
PING 8.8.8.8 (8.8.8.8) from 192.168.42.1 : 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=59 time=28.6 ms
...
phone# tcpdump -i rndis0 -n icmp or arp
IP 192.168.42.1 > 8.8.8.8: ICMP echo request, id 24969, seq 1, length 64
IP 8.8.8.8 > 192.168.42.1: ICMP echo reply, id 24969, seq 1, length 64
它有效,因为我们使用的是地址,它正确地匹配了 ip 规则。
现在我们使用界面再次尝试相同的 ping:
desktop$ ping 8.8.8.8 -I usb0
PING 8.8.8.8 (8.8.8.8) from 192.168.42.1 usb0: 56(84) bytes of data.
^C
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
...
phone# tcpdump -i rndis0 -n icmp or arp
ARP, Request who-has 8.8.8.8 tell 192.168.42.1, length 28
失败了;下一跳没有interface路由,所以它会再次发出ARP请求,永远不会得到回复。所以我们需要为 interface 添加一个 ip 规则,以使用 192.168.42.129 作为下一跳:
desktop$ sudo ip rule add oif usb0 lookup T1
desktop$ ping 8.8.8.8 -I usb0
PING 8.8.8.8 (8.8.8.8) from 192.168.42.1 usb0: 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=59 time=10.7 ms
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 10.791/10.791/10.791/0.000 ms
...
phone# tcpdump -i rndis0 -n icmp or arp
IP 192.168.42.1 > 8.8.8.8: ICMP echo request, id 24979, seq 1, length 64
IP 8.8.8.8 > 192.168.42.1: ICMP echo reply, id 24979, seq 1, length 64
我相信,一般来说,缺少接口路由不会对您的正常、非接口绑定、传出连接的实现产生负面影响。大多数(不是全部)应用程序绑定到 address 以进行出站 TCP/UDP 连接,并且仅绑定到 interface 以进行传入连接(侦听)。 ping 实用程序是一种特殊情况。
为了证明这一点,如果我从路由策略中删除接口规则,我仍然能够在指定绑定 地址 时使用正常的出站套接字。在下面的示例中,我使用 telnet 和 netcat,在这两种情况下都指定了绑定地址 (-b 192.168.42.1),它与 T1 表正确匹配,因此使用网关。
# remove the interface route, keep the address route
desktop$ sudo ip rule del from all oif usb0 lookup T1
desktop$ nc -zv 8.8.8.8 443 -s 192.168.42.1
google-public-dns-a.google.com [8.8.8.8] 443 (https) open
phone# tcpdump -i rndis0 -n host 8.8.8.8
IP 192.168.42.1.40785 > 8.8.8.8.443: Flags [S], seq 1678217252, win 29200, options [mss 1460,sackOK,TS val 20223895 ecr 0,nop,wscale 6], length 0
IP 8.8.8.8.443 > 192.168.42.1.40785: Flags [S.], seq 86178051, ack 1678217253, win 28400, options [mss 1432,sackOK,TS val 1937335284 ecr 20223895,nop,wscale 8], length 0
....
desktop$ telnet 8.8.8.8 53 -b 192.168.42.1
...
phone# tcpdump -i rndis0 -n host 8.8.8.8
IP 192.168.42.1.57109 > 8.8.8.8.53: Flags [.], ack 1, win 457, options [nop,nop,TS val 20288983 ecr 4154032957], length 0
IP 8.8.8.8.53 > 192.168.42.1.57109: Flags [F.], seq 1, ack 1, win 111, options [nop,nop,TS val 4154033968 ecr 20288983], length 0
我在测试策略路由实现时遇到了同样的问题,我有点想明白为什么我的接口 ping 没有得到答复。希望这可以解决问题。