Answer:
您需要添加输出接口规则(ip rule add oif ...
) 此外ip rule add from ...
,因为 ping 绑定到界面,而不是IP。
Example:
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>
),没有任何匹配的策略路由。因此 ping 的行为与没有为该地址定义路由时的情况完全相同界面 at all.
测试/证明示例(在没有策略路由的情况下开始):
使用我的手机 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 的广播域内address。然而,随着ping 192.168.42.129 -I usb0
-- 我告诉 ping 使用界面本身,并且没有与该接口匹配的路由(因此,没有广播域的概念),因此它会盲目地对任何不属于自己的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 网络外的某些内容时,同样的概念也适用;如果我使用以下命令 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
使用界面时address,路由表中缺少任何类型的下一跳路由都会导致其失败并且无法发出 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
它有效,因为我们正在使用address,它正确匹配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
它失败;没有界面下一跳的路由,因此它会再次发出 ARP 请求,但永远不会得到回复。所以我们需要添加一条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 连接,并且仅绑定到界面用于传入连接(侦听)。 ping 实用程序是一个特例。
为了证明这一点,如果我从路由策略中删除接口规则,在指定绑定时我仍然可以使用正常的出站套接字address。在下面的示例中,我使用 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 没有得到答复。希望这能澄清一切。