Mar*_*tin 9 networking linux-kernel arp
我有一台PC(内核3.2.0-23泛型)已192.168.1.2/24配置为eth0接口,并且也使用192.168.1.1和192.168.1.2地址tun0接口:
root@T42:~# ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:16:41:54:01:93 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.2/24 scope global eth0
inet6 fe80::216:41ff:fe54:193/64 scope link
valid_lft forever preferred_lft forever
3: bond0: <BROADCAST,MULTICAST,MASTER> mtu 1500 qdisc noop state DOWN
link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
4: irda0: <NOARP> mtu 2048 qdisc noop state DOWN qlen 8
link/irda 00:00:00:00 brd ff:ff:ff:ff
5: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:13:ce:8b:99:3e brd ff:ff:ff:ff:ff:ff
inet 10.30.51.53/24 brd 10.30.51.255 scope global eth1
inet6 fe80::213:ceff:fe8b:993e/64 scope link
valid_lft forever preferred_lft forever
6: tun0: <POINTOPOINT,MULTICAST,NOARP> mtu 1500 qdisc pfifo_fast state DOWN qlen 100
link/none
inet 192.168.1.1 peer 192.168.1.2/32 scope global tun0
root@T42:~# ip route show dev eth0
192.168.1.0/24 proto kernel scope link src 192.168.1.2
root@T42:~#
Run Code Online (Sandbox Code Playgroud)
如上所示,tun0在管理上被禁用(ip link set dev tun0 down)。现在,当我收到 ARP 请求时192.168.1.2,PC 不会回复这些请求:
root@T42:~# tcpdump -nei eth0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
15:30:34.875427 00:1a:e2:ae:cb:b7 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 60: Request who-has 192.168.1.2 tell 192.168.1.1, length 46
15:30:36.875268 00:1a:e2:ae:cb:b7 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 60: Request who-has 192.168.1.2 tell 192.168.1.1, length 46
15:30:39.138651 00:1a:e2:ae:cb:b7 > 00:1a:e2:ae:cb:b7, ethertype Loopback (0x9000), length 60:
^C
3 packets captured
3 packets received by filter
0 packets dropped by kernel
root@T42:~#
Run Code Online (Sandbox Code Playgroud)
只有在我删除tun0接口(ip link del dev tun0)后,PC 才会回复接口192.168.1.2上的ARP 请求eth0。
路由表在之前和之后看起来完全一样ip link del dev tun0:
root@T42:~# netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 10.30.51.254 0.0.0.0 UG 0 0 0 eth1
10.30.51.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1
192.168.1.0 192.168.1.2 255.255.255.0 UG 0 0 0 eth0
root@T42:~# ip link del dev tun0
root@T42:~# netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 10.30.51.254 0.0.0.0 UG 0 0 0 eth1
10.30.51.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1
192.168.1.0 192.168.1.2 255.255.255.0 UG 0 0 0 eth0
root@T42:~#
Run Code Online (Sandbox Code Playgroud)
下面的路由条目已使用ip link set dev tun0 down命令删除:
Destination Gateway Genmask Flags MSS Window irtt Iface
192.168.1.2 0.0.0.0 255.255.255.255 UH 0 0 0 tun0
Run Code Online (Sandbox Code Playgroud)
然而,虽然路由表在ip link del dev tun0命令之前和之后完全相同,但内核将做出的实际路由决策不是:
T42:~# ip route get 192.168.1.1
local 192.168.1.1 dev lo src 192.168.1.1
cache <local>
T42:~# ip link del dev tun0
T42:~# ip route get 192.168.1.1
192.168.1.1 dev eth0 src 192.168.1.2
cache ipid 0x8390
T42:~#
Run Code Online (Sandbox Code Playgroud)
这是预期的行为吗?为什么内核忽略路由表?
Jan*_*der 20
确切地说,您的路由表并没有被忽略。它被更高优先级的路由表否决。
您在键入时看到的路由表ip route show并不是内核使用的唯一路由表。其实默认有3个路由表,按照命令显示的顺序查找ip rule:
# ip rule show
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
Run Code Online (Sandbox Code Playgroud)
您最熟悉的表是main,但最高优先级的路由表是local。该表由内核管理以跟踪本地和广播路由:换句话说,该local表告诉内核如何路由到它自己的接口的地址。它看起来像这样:
# 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 192.168.1.0 dev eth0 proto kernel scope link src 192.168.1.2
local 192.168.1.1 dev tun0 proto kernel scope host src 192.168.1.1
local 192.168.1.2 dev eth0 proto kernel scope host src 192.168.1.2
broadcast 192.168.1.255 dev eth0 proto kernel scope link src 192.168.1.2
Run Code Online (Sandbox Code Playgroud)
查看该行引用tun0。这就是导致您从route get. 它说 192.168.1.1 是一个本地地址,这意味着如果我们想向 192.168.1.1 发送 ARP 回复,这很容易;我们把它寄给自己。既然我们在local表中找到了一条路线,我们就不再搜索路线了,也不用费心去检查main或default表了。
至少,能够打字ip route而不会看到所有那些杂乱无章的“明显”路线是很好的(尝试route print在 Windows 机器上打字)。它还可以作为一些针对错误配置的最低限度的保护:即使主路由表被混淆了,内核仍然知道如何与自己对话。
(为什么首先要保留本地路由?因此内核可以对本地地址使用与其他所有内容相同的查找代码。它使内部变得更简单。)
您还可以使用此多表方案做其他有趣的事情。特别是,您可以添加自己的表,并指定搜索规则。这称为“策略路由”,如果您曾经想根据源地址路由数据包,这就是在 Linux 中的做法。
如果您正在做特别棘手或实验性的事情,您可以local通过table local在ip route命令中指定自己添加或删除路由。但是,除非您知道自己在做什么,否则很可能会混淆内核。当然,内核仍会继续添加和删除自己的路由,因此您必须注意确保您的路由不会被覆盖。
最后,如果您想一次查看所有路由表:
# ip route show table all
Run Code Online (Sandbox Code Playgroud)
有关更多信息,请查看ip-rule(8)手册页或iproute2 文档。您还可以尝试高级路由和流量控制 HOWTO中的一些示例,了解您可以做什么。